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

  1. Retry the transaction: re-read the key at its new version and re-apply the update
  2. Reduce contention by partitioning keys or serializing access to hot keys
  3. In tests, join concurrent goroutines or use distinct key sets per transaction
  4. 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

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


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/4da63966086fad37. Report an issue: GitHub.