56 lines
1.5 KiB
Go
56 lines
1.5 KiB
Go
package store
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
)
|
|
|
|
// TestConcurrentReadWrite تأیید میکند که با pool چند-اتصالی + WAL + busy_timeout،
|
|
// خواندن و نوشتنِ همزمان بدونِ خطای «database is locked» انجام میشود.
|
|
func TestConcurrentReadWrite(t *testing.T) {
|
|
s, err := Open(t.TempDir() + "/c.db")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer s.Close()
|
|
if _, err := s.DB.Exec(`CREATE TABLE t(id INTEGER PRIMARY KEY, v INTEGER)`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
const workers = 40 // ۲۰ نویسنده + ۲۰ خواننده همزمان
|
|
const iters = 50
|
|
var wg sync.WaitGroup
|
|
var writeErr, readErr int64
|
|
for w := 0; w < workers; w++ {
|
|
wg.Add(1)
|
|
go func(w int) {
|
|
defer wg.Done()
|
|
for i := 0; i < iters; i++ {
|
|
if w%2 == 0 {
|
|
if _, err := s.DB.Exec(`INSERT INTO t(v) VALUES(?)`, i); err != nil {
|
|
atomic.AddInt64(&writeErr, 1)
|
|
t.Logf("write err: %v", err)
|
|
}
|
|
} else {
|
|
var n int
|
|
if err := s.DB.QueryRow(`SELECT COUNT(*) FROM t`).Scan(&n); err != nil {
|
|
atomic.AddInt64(&readErr, 1)
|
|
t.Logf("read err: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}(w)
|
|
}
|
|
wg.Wait()
|
|
|
|
if writeErr != 0 || readErr != 0 {
|
|
t.Fatalf("concurrency errors: writes=%d reads=%d (expected 0)", writeErr, readErr)
|
|
}
|
|
var total int
|
|
_ = s.DB.QueryRow(`SELECT COUNT(*) FROM t`).Scan(&total)
|
|
if want := (workers / 2) * iters; total != want {
|
|
t.Fatalf("expected %d rows written, got %d", want, total)
|
|
}
|
|
}
|