| 246 | } |
| 247 | |
| 248 | func TestMergeV2BasicFlow(t *testing.T) { |
| 249 | dir := t.TempDir() |
| 250 | opts := DefaultOptions |
| 251 | opts.Dir = dir |
| 252 | opts.SegmentSize = 8 * 1024 // 8KB - small segment to force multiple files |
| 253 | opts.EnableMergeV2 = true |
| 254 | |
| 255 | db, err := Open(opts) |
| 256 | if err != nil { |
| 257 | t.Fatalf("Open failed: %v", err) |
| 258 | } |
| 259 | defer func() { |
| 260 | if err := db.Close(); err != nil { |
| 261 | t.Errorf("Close failed: %v", err) |
| 262 | } |
| 263 | }() |
| 264 | |
| 265 | bucket := "test_bucket" |
| 266 | |
| 267 | // Create bucket first |
| 268 | if err := db.Update(func(tx *Tx) error { |
| 269 | return tx.NewBucket(DataStructureBTree, bucket) |
| 270 | }); err != nil { |
| 271 | t.Fatalf("NewBucket failed: %v", err) |
| 272 | } |
| 273 | |
| 274 | // Write enough data to create multiple files |
| 275 | // Each entry is roughly 100+ bytes, so 200 entries should create multiple 8KB files |
| 276 | for i := 0; i < 200; i++ { |
| 277 | key := []byte(fmt.Sprintf("key_%05d", i)) |
| 278 | value := make([]byte, 100) // 100 bytes value |
| 279 | for j := range value { |
| 280 | value[j] = byte(i % 256) |
| 281 | } |
| 282 | if err := db.Update(func(tx *Tx) error { |
| 283 | return tx.Put(bucket, key, value, 0) |
| 284 | }); err != nil { |
| 285 | t.Fatalf("Put failed: %v", err) |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | // Update some keys to create redundancy |
| 290 | for i := 0; i < 50; i++ { |
| 291 | key := []byte(fmt.Sprintf("key_%05d", i)) |
| 292 | value := make([]byte, 100) |
| 293 | for j := range value { |
| 294 | value[j] = byte((i + 200) % 256) // Different pattern for updated values |
| 295 | } |
| 296 | if err := db.Update(func(tx *Tx) error { |
| 297 | return tx.Put(bucket, key, value, 0) |
| 298 | }); err != nil { |
| 299 | t.Fatalf("Update failed: %v", err) |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | // Delete some keys |
| 304 | for i := 50; i < 60; i++ { |
| 305 | key := []byte(fmt.Sprintf("key_%05d", i)) |