juicedata/juicefs · warning
write conflict: %s %d > %d
Error message
write conflict: %s %d > %d
What it means
Same commit-time optimistic-concurrency check in tkv_mem.go txn(): this variant fires when the key still exists but its current version (it.ver) is greater than the version the transaction observed (tx.observed[k]), meaning another transaction modified the key after this one read it. The transaction is rejected to preserve serializability.
Source
Thrown at pkg/meta/tkv_mem.go:259
store: c,
observed: make(map[string]int),
buffer: make(map[string][]byte),
}
if err := f(&kvTxn{tx, retry}); err != nil {
return err
}
if len(tx.buffer) == 0 {
return nil
}
c.Lock()
defer c.Unlock()
for k, ver := range tx.observed {
it := c.get(k)
if it == nil && ver != 0 {
return fmt.Errorf("write conflict: %s was version %d, now deleted", k, ver)
} else if it != nil && it.ver > ver {
return fmt.Errorf("write conflict: %s %d > %d", k, it.ver, ver)
}
}
if _, ok := tx.buffer["setting"]; ok {
d, _ := json.Marshal(tx.buffer)
if err := os.WriteFile(settingPath, d, 0644); err != nil {
return err
}
}
for k, value := range tx.buffer {
c.set(k, value)
}
return nil
}
func (c *memKV) scan(prefix []byte, handler func(key []byte, value []byte) bool) error {
c.Lock()
snap := c.items.Clone()
c.Unlock()View on GitHub (pinned to c9a67b23e8)
Solutions
- Retry the transaction: re-read the key at its new version and re-apply the update
- Reduce contention by partitioning keys or serializing access to hot keys
- In tests, join concurrent goroutines or use distinct key sets per transaction
- Upgrade to a real engine (redis/sql/tkv) for production concurrency instead of mem store
Example fix
// before
if err := tx.commit(); err != nil { return err }
// after
if err := tx.commit(); err != nil {
if strings.Contains(err.Error(), "write conflict") { return retryTxn() }
return err
} Defensive patterns
Strategy: retry
Try / catch
if err := tx.commit(); err != nil {
if strings.Contains(err.Error(), "write conflict") { return retryWithFreshTxn() }
return err
} Prevention
- Keep transactions short to reduce overlapping read/write windows on hot keys
- Use distinct key sets per concurrent worker in tests
- Treat any "write conflict" error as retryable by design
When it happens
Trigger: tx A reads key k at version 5 and buffers a write; tx B updates k (version becomes 6) and commits; tx A commits and the check `it.ver (6) > ver (5)` returns this error.
Common situations: Concurrent kvMeta transactions in tests over the mem engine; concurrent metadata updates to the same inode from two sessions; benchmark code racing writers.
Related errors
- write conflict: %s was version %d, now deleted
- concurrent staging limit reached
- object key cannot be empty
- multipart upload error: %s
- errUnstableCoLimit
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/4da63966086fad37.
Report an issue: GitHub.