From 138b5edc655113ba17a54fe2d448804517938b48 Mon Sep 17 00:00:00 2001 From: ryyst Date: Wed, 10 Sep 2025 07:36:03 +0300 Subject: [PATCH] Add conflict resolution testing and verify functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added: - test_conflict.go utility to create timestamp collision scenarios - Verified sophisticated conflict resolution works correctly Test Results: ✅ Successfully created conflicting data with identical timestamps ✅ Conflict resolution triggered during sync cycle ✅ Majority vote system activated (2-node scenario) ✅ Oldest node tie-breaker correctly applied ✅ Remote data won based on older joined timestamp ✅ Local data was properly replaced with winning version ✅ Detailed logging showed complete decision process Logs showed the complete flow: 1. "Timestamp collision detected, starting conflict resolution" 2. "Starting conflict resolution with majority vote" 3. "Resolved conflict using oldest node tie-breaker" 4. "Conflict resolved: remote data wins" 5. "Conflict resolved, updated local data" The sophisticated conflict resolution system works exactly as designed! 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- test_conflict.go | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 test_conflict.go diff --git a/test_conflict.go b/test_conflict.go new file mode 100644 index 0000000..812ba67 --- /dev/null +++ b/test_conflict.go @@ -0,0 +1,101 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "time" + + badger "github.com/dgraph-io/badger/v4" + "github.com/google/uuid" +) + +// StoredValue matches the structure in main.go +type StoredValue struct { + UUID string `json:"uuid"` + Timestamp int64 `json:"timestamp"` + Data json.RawMessage `json:"data"` +} + +// Test utility to create conflicting data directly in BadgerDB +func createConflictingData(dataDir1, dataDir2 string) error { + // Same timestamp, different UUIDs + timestamp := time.Now().UnixMilli() + path := "test/conflict/data" + + // Data for node1 + data1 := json.RawMessage(`{"message": "from node1", "value": 100}`) + uuid1 := uuid.New().String() + + // Data for node2 (same timestamp, different UUID and content) + data2 := json.RawMessage(`{"message": "from node2", "value": 200}`) + uuid2 := uuid.New().String() + + // Store in node1's database + err := storeConflictData(dataDir1, path, timestamp, uuid1, data1) + if err != nil { + return fmt.Errorf("failed to store in node1: %v", err) + } + + // Store in node2's database + err = storeConflictData(dataDir2, path, timestamp, uuid2, data2) + if err != nil { + return fmt.Errorf("failed to store in node2: %v", err) + } + + fmt.Printf("Created conflict scenario:\n") + fmt.Printf("Path: %s\n", path) + fmt.Printf("Timestamp: %d\n", timestamp) + fmt.Printf("Node1 UUID: %s, Data: %s\n", uuid1, string(data1)) + fmt.Printf("Node2 UUID: %s, Data: %s\n", uuid2, string(data2)) + + return nil +} + +func storeConflictData(dataDir, path string, timestamp int64, uuid string, data json.RawMessage) error { + opts := badger.DefaultOptions(dataDir + "/badger") + opts.Logger = nil + db, err := badger.Open(opts) + if err != nil { + return err + } + defer db.Close() + + storedValue := StoredValue{ + UUID: uuid, + Timestamp: timestamp, + Data: data, + } + + valueBytes, err := json.Marshal(storedValue) + if err != nil { + return err + } + + return db.Update(func(txn *badger.Txn) error { + // Store main data + if err := txn.Set([]byte(path), valueBytes); err != nil { + return err + } + + // Store timestamp index + indexKey := fmt.Sprintf("_ts:%020d:%s", timestamp, path) + return txn.Set([]byte(indexKey), []byte(uuid)) + }) +} + +func main() { + if len(os.Args) < 3 { + fmt.Println("Usage: go run test_conflict.go ") + os.Exit(1) + } + + err := createConflictingData(os.Args[1], os.Args[2]) + if err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } + + fmt.Println("Conflict data created successfully!") + fmt.Println("Start your nodes and trigger a sync to see conflict resolution in action.") +} \ No newline at end of file