Put/Delete checked writeStopped then called queue.Submit (which sends on
a channel). Close set writeStopped then closed the channel. With no
synchronization between the check and Submit, a concurrent Close could
close the channel during the window, causing "send on closed channel"
panic in Put/Delete.
The CommitQueue.Close comment said "Callers must stop submitting before
Close" but Put/Delete didn't enforce this.
Changes:
- wal/writer.go: add submitMu sync.RWMutex. Put/Delete construct entry
outside the lock, then hold RLock during writeStopped check + Submit;
release before <-req.Result. Close holds write lock during
writeStopped.Store + queue.Close, guaranteeing no Submit is in progress
when the channel is closed.
- wal/writer_test.go: add TestConcurrentPutCloseNoPanic and
TestConcurrentDeleteCloseNoPanic. 100 goroutines + 1 closer, recover
panics. Run with -count=50 for regression detection.
Verified: all existing tests pass. New concurrent tests pass with
-count=50 (0 panics). go test -race ./... green.
Audit context: docs/audit-3.2.md C7.
- Add TestWriteStoppedAfterIOError verifying ErrCommitUnknown → write-stopped
transition after I/O failure (DoD requirement)
- Replace VKValueLogPointer with VKInline in segment_writer_test.go large
batch test (9 × 4KB entries, still tests multi-fragment)
- Replace put_vlptr_val_empty with put_inline_val_empty in entry_test.go
- VKValueLogPointer remains only in constants_test.go (enum value check)
- wal/commit_queue.go: bounded buffered channel for write requests
- wal/writer.go: single-goroutine main loop implementing 11-step write flow
with group commit, sequence allocation, MemTable publish/abort, write-stopped
- wal/recovery.go: BatchReplayer interface, ReplayBatch, ReplaySegmentFile,
RecoverFromSegments with fragment reassembly and tail corruption handling
- Comprehensive tests for all modules, all pass with -race