Add conflict resolution testing and verify functionality

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 <noreply@anthropic.com>
This commit is contained in:
2025-09-10 07:36:03 +03:00
parent e5c9dbc7d8
commit 138b5edc65

101
test_conflict.go Normal file
View File

@ -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 <data_dir1> <data_dir2>")
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.")
}