Compare commits

..

No commits in common. "si" and "master" have entirely different histories.
si ... master

50
main.go
View File

@ -26,7 +26,6 @@ type Store struct {
type Transaction struct { type Transaction struct {
store *Store store *Store
readTS Timestamp readTS Timestamp
readSet map[string]struct{}
writeBuffer map[string]string writeBuffer map[string]string
committed bool committed bool
} }
@ -66,13 +65,11 @@ func (s *Store) Begin() *Transaction {
return &Transaction{ return &Transaction{
store: s, store: s,
readTS: readTS, readTS: readTS,
readSet: make(map[string]struct{}),
writeBuffer: make(map[string]string), writeBuffer: make(map[string]string),
} }
} }
func (tx *Transaction) Read(key string) (string, bool) { func (tx *Transaction) Read(key string) (string, bool) {
tx.readSet[key] = struct{}{}
if val, ok := tx.writeBuffer[key]; ok { if val, ok := tx.writeBuffer[key]; ok {
return val, true return val, true
} }
@ -88,40 +85,27 @@ func (tx *Transaction) Commit() error {
return fmt.Errorf("transaction already committed") return fmt.Errorf("transaction already committed")
} }
s := tx.store tx.store.mu.Lock()
s.mu.Lock() defer tx.store.mu.Unlock()
defer s.mu.Unlock()
getLatestTS := func(key string) Timestamp { // Conflict detection
if h, ok := s.data[key]; ok && len(h.Versions) > 0 {
return h.Versions[len(h.Versions)-1].Timestamp
}
return 0
}
// Write-write Conflict detection
for key := range tx.writeBuffer { for key := range tx.writeBuffer {
if getLatestTS(key) > tx.readTS { history, exists := tx.store.data[key]
return fmt.Errorf("write-write conflict on key: %s", key) if !exists {
}
}
// Read-write conflict detection (excluding keys we're writing)
for key := range tx.readSet {
if _, isWriting := tx.writeBuffer[key]; isWriting {
continue continue
} }
if getLatestTS(key) > tx.readTS { latest := history.Versions[len(history.Versions)-1]
return fmt.Errorf("read-write conflict on key: %s", key) if latest.Timestamp > tx.readTS {
return fmt.Errorf("write-write conflict on key '%s'", key)
} }
} }
commitTS := tx.store.nextTimestamp() commitTS := tx.store.nextTimestamp()
for key, val := range tx.writeBuffer { for key, val := range tx.writeBuffer {
history, exists := s.data[key] history, exists := tx.store.data[key]
if !exists { if !exists {
history = &KeyHistory{} history = &KeyHistory{}
s.data[key] = history tx.store.data[key] = history
} }
history.Versions = append(history.Versions, VersionedValue{ history.Versions = append(history.Versions, VersionedValue{
Value: val, Value: val,
@ -155,9 +139,10 @@ func main() {
fmt.Println("tx2 reads foo: not found") fmt.Println("tx2 reads foo: not found")
} }
// Transaction 3: Conflicting write — modifies "foo" after tx2 read it // Transaction 3: Conflicting write
tx3 := store.Begin() tx3 := store.Begin()
tx3.Write("foo", "baz") tx3.Write("foo", "baz")
// tx2 committed first, so tx3 will conflict if tx2 also wrote to "foo"
err = tx3.Commit() err = tx3.Commit()
if err != nil { if err != nil {
fmt.Println("tx3 commit error:", err) fmt.Println("tx3 commit error:", err)
@ -165,21 +150,11 @@ func main() {
fmt.Println("tx3 committed") fmt.Println("tx3 committed")
} }
// Now tx2 tries to commit after reading a value that's been modified — should conflict
tx2.Write("bar", "new") // tx2 tries to do something unrelated, but it read "foo"
err = tx2.Commit()
if err != nil {
fmt.Println("tx2 commit error (expected read-write conflict):", err)
} else {
fmt.Println("tx2 committed (unexpected!)")
}
// Transaction 4: Should read latest "foo" // Transaction 4: Should read latest "foo"
tx4 := store.Begin() tx4 := store.Begin()
val, _ = tx4.Read("foo") val, _ = tx4.Read("foo")
fmt.Printf("tx4 reads foo: %s\n", val) fmt.Printf("tx4 reads foo: %s\n", val)
// Additional write-write conflict test (unchanged)
tx5 := store.Begin() tx5 := store.Begin()
tx6 := store.Begin() tx6 := store.Begin()
@ -197,4 +172,3 @@ func main() {
fmt.Println("tx6 committed (unexpectedly)") fmt.Println("tx6 committed (unexpectedly)")
} }
} }