Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
a88d378151 |
50
main.go
50
main.go
@ -26,6 +26,7 @@ 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
|
||||||
}
|
}
|
||||||
@ -65,11 +66,13 @@ 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
|
||||||
}
|
}
|
||||||
@ -85,27 +88,40 @@ func (tx *Transaction) Commit() error {
|
|||||||
return fmt.Errorf("transaction already committed")
|
return fmt.Errorf("transaction already committed")
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.store.mu.Lock()
|
s := tx.store
|
||||||
defer tx.store.mu.Unlock()
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
// Conflict detection
|
getLatestTS := func(key string) Timestamp {
|
||||||
|
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 {
|
||||||
history, exists := tx.store.data[key]
|
if getLatestTS(key) > tx.readTS {
|
||||||
if !exists {
|
return fmt.Errorf("write-write conflict on key: %s", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read-write conflict detection (excluding keys we're writing)
|
||||||
|
for key := range tx.readSet {
|
||||||
|
if _, isWriting := tx.writeBuffer[key]; isWriting {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
latest := history.Versions[len(history.Versions)-1]
|
if getLatestTS(key) > tx.readTS {
|
||||||
if latest.Timestamp > tx.readTS {
|
return fmt.Errorf("read-write conflict on key: %s", key)
|
||||||
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 := tx.store.data[key]
|
history, exists := s.data[key]
|
||||||
if !exists {
|
if !exists {
|
||||||
history = &KeyHistory{}
|
history = &KeyHistory{}
|
||||||
tx.store.data[key] = history
|
s.data[key] = history
|
||||||
}
|
}
|
||||||
history.Versions = append(history.Versions, VersionedValue{
|
history.Versions = append(history.Versions, VersionedValue{
|
||||||
Value: val,
|
Value: val,
|
||||||
@ -139,10 +155,9 @@ func main() {
|
|||||||
fmt.Println("tx2 reads foo: not found")
|
fmt.Println("tx2 reads foo: not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction 3: Conflicting write
|
// Transaction 3: Conflicting write — modifies "foo" after tx2 read it
|
||||||
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)
|
||||||
@ -150,11 +165,21 @@ 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()
|
||||||
|
|
||||||
@ -172,3 +197,4 @@ func main() {
|
|||||||
fmt.Println("tx6 committed (unexpectedly)")
|
fmt.Println("tx6 committed (unexpectedly)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user