fix: add per-hash mutex to prevent concurrent CAS writes
Some checks failed
CI / lint (push) Successful in 11m11s
CI / test (push) Successful in 10m55s
CI / build (push) Failing after 5m36s
CI / docker (push) Has been skipped

StoreFile now uses a per-hash sync.Mutex to prevent race conditions
when multiple workers (launcher fetcher or parallel uploads) write
the same file simultaneously. Duplicate writes are idempotent —
if another goroutine stored the file while we waited, return the
existing hash without re-writing.
This commit is contained in:
2026-06-01 06:51:54 +03:00
parent 7b3b97c5f8
commit 3499963205
2 changed files with 83 additions and 0 deletions

View File

@@ -1,8 +1,12 @@
package cas
import (
"crypto/sha1"
"encoding/hex"
"os"
"path/filepath"
"sync"
"sync/atomic"
"testing"
)
@@ -68,6 +72,45 @@ func TestStoreFile_Duplicate(t *testing.T) {
}
}
func TestStoreFile_ConcurrentSameHash(t *testing.T) {
dir := t.TempDir()
data := []byte("concurrent write test")
const workers = 10
var success int64
var wg sync.WaitGroup
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer wg.Done()
hash, err := StoreFile(dir, data)
if err != nil {
t.Errorf("StoreFile failed: %v", err)
return
}
if len(hash) != 40 {
t.Errorf("invalid hash length: %d", len(hash))
return
}
atomic.AddInt64(&success, 1)
}()
}
wg.Wait()
if success != workers {
t.Errorf("expected %d successes, got %d", workers, success)
}
// All goroutines must produce the same hash for identical data.
h := sha1.Sum(data)
hash := hex.EncodeToString(h[:])
if !FileExists(dir, hash) {
t.Error("file not found after concurrent writes")
}
}
func TestFileExists(t *testing.T) {
dir := t.TempDir()
data := []byte("test data")