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:
		
							
								
								
									
										101
									
								
								test_conflict.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								test_conflict.go
									
									
									
									
									
										Normal 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.")
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user