Skip to content

Thread safety/sync pool solution#17

Merged
shaia merged 19 commits into
mainfrom
thread-safety/sync-pool-solution
Nov 2, 2025
Merged

Thread safety/sync pool solution#17
shaia merged 19 commits into
mainfrom
thread-safety/sync-pool-solution

Conversation

@shaia
Copy link
Copy Markdown
Owner

@shaia shaia commented Nov 1, 2025

No description provided.

shaia added 2 commits November 1, 2025 10:02
- Add 100% coverage unit tests for hash functions (230+ test cases)
- Add integration tests for stress testing, edge cases, and concurrency
- Add thread safety analysis documenting 4 race conditions
- Add test coverage summary documentation
- Refactor stress tests to separate concurrent tests
- Skip concurrent tests with documented reason until fixed

New test files:
- internal/hash/hash_test.go (100% coverage)
- tests/integration/bloomfilter_stress_test.go (refactored)
- tests/integration/bloomfilter_concurrent_test.go (new)
- tests/integration/bloomfilter_edge_cases_test.go (new)
- tests/integration/bloomfilter_race_test.go (new)

Documentation:
- THREAD_SAFETY_ANALYSIS.md - detailed race condition analysis
- tests/TEST_COVERAGE_SUMMARY.md - complete test coverage overview

Known issue: BloomFilter has thread-safety bugs in storage layer
(storage.go:174) causing nil pointer dereference in concurrent scenarios.
All concurrent tests skip until fixed.
…ions

This commit implements a comprehensive thread-safety solution using sync.Pool,
atomic operations, stack allocation optimization, and batch APIs.

Thread-Safety Implementation:
- Removed shared mutable state (bf.positions, bf.cacheLineIndices fields)
- Implemented sync.Pool for per-operation temporary storage (OperationStorage)
- Added atomic.CompareAndSwapUint64 for lock-free bit setting
- Added atomic.LoadUint64 for thread-safe bit reading
- Each operation now has completely isolated position data

Performance Optimizations:
- Stack allocation for common case (hashCount ≤ 8, covers ~90% of filters)
- Batch API methods: AddBatch, AddBatchString, AddBatchUint64
- Zero-allocation batch operations (reuses position buffers)
- 38-50% fewer allocations compared to alternatives

Storage Layer Refactoring:
- Complete rewrite of internal/storage/storage.go
- New OperationStorage struct for pooled per-operation storage
- Separate sync.Pool instances for array/map modes
- Efficient clear() method that only resets used indices

Test Suite Updates:
- Enabled previously skipped concurrent tests (all now pass)
- Rewrote storage tests for new OperationStorage architecture
- Added TestConcurrentPoolAccess for pool safety verification

Performance Results:
- Add operations: 768.7 ns/op (competitive with willf/bloom: 769.7 ns/op)
- Concurrent reads: 7.2M reads/sec
- Concurrent writes: 15.2M writes/sec
- Mixed operations: 20.7M ops/sec
- Batch operations: 12.5% faster with zero allocations
- Memory efficiency: 38% fewer allocations than willf/bloom
@shaia shaia requested a review from Copilot November 1, 2025 16:05
@shaia shaia self-assigned this Nov 1, 2025
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements comprehensive thread-safety improvements and extensive testing for a cache-optimized Bloom Filter implementation. The key change is migrating from shared mutable state to a sync.Pool-based approach for operation storage, enabling lock-free concurrent operations. Additionally, atomic operations are introduced for bit manipulation to ensure thread-safe reads and writes.

Key Changes

  • Refactored storage layer to use sync.Pool for per-operation storage, eliminating shared mutable state
  • Added atomic operations (LoadUint64, CompareAndSwapUint64) for thread-safe bit manipulation
  • Introduced comprehensive test suite including stress tests, edge case validation, race condition detection tests, and concurrency tests

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
internal/storage/storage.go Complete refactor to introduce OperationStorage with sync.Pool for thread-safe per-operation temporary storage
internal/storage/storage_test.go Updated tests to use new pool-based API; removed tests for removed shared state fields
bloomfilter.go Modified to use pool-based storage and atomic operations; removed pre-allocated shared arrays; added batch operations
tests/integration/bloomfilter_stress_test.go New comprehensive stress tests covering large datasets (1M-10M elements), performance, and memory footprint
tests/integration/bloomfilter_edge_cases_test.go New edge case tests for boundary conditions, hash distribution, collision resistance, and Unicode handling
tests/integration/bloomfilter_race_test.go New race detection tests with // +build race tag for concurrent operation validation
tests/integration/bloomfilter_concurrent_test.go New concurrency tests for validating thread-safe reads, writes, and mixed operations
THREAD_SAFETY_ANALYSIS.md Detailed documentation of previous thread-safety issues and the implemented solution
internal/hash/hash_test.go New comprehensive unit tests achieving 100% coverage of hash functions
tests/TEST_COVERAGE_SUMMARY.md Documentation of test coverage across all packages
results/README.md Updated timestamp for latest benchmark run

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go Outdated
Comment thread internal/storage/storage.go Outdated
Comment thread bloomfilter.go Outdated
Comment thread tests/integration/bloomfilter_race_test.go Outdated
Comment thread bloomfilter.go
Comment on lines +389 to +395
func (bf *CacheOptimizedBloomFilter) getHashPositionsOptimized(data []byte) ([]uint64, []uint64) {
h1 := hash.Optimized1(data)
h2 := hash.Optimized2(data)

// Clear the hash map efficiently
bf.storage.ClearHashMap()
// Get operation storage from pool (thread-safe)
ops := storage.GetOperationStorage(bf.storage.UseArrayMode)
defer storage.PutOperationStorage(ops)
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function returns ops.GetUsedHashIndices() which is a slice from the pooled OperationStorage, but the storage is returned to the pool immediately after via defer. This means the returned slice's backing array could be reused by another goroutine before the caller finishes using it, potentially causing data corruption. The caller should copy the slice or the function should not use defer for putting storage back.

Copilot uses AI. Check for mistakes.
…tions

This commit fixes a critical data corruption bug and implements several
performance and safety improvements based on code review feedback.

Critical Bug Fix:
- FIXED: getHashPositionsOptimized() was returning slice from pooled storage
  that was immediately returned to pool via defer. This caused data corruption
  as the backing array could be reused by another goroutine before the caller
  finished using it. Now copies the slice before returning.

Performance Optimizations:
- Moved clear() from GetOperationStorage() to PutOperationStorage() to avoid
  redundant clearing since pool's New function already returns clean objects
- Optimized AddBatchString() to process strings directly without intermediate
  [][]byte allocation, eliminating double iteration

Safety Improvements:
- Added defer for PutOperationStorage() in batch operations for panic safety
- Added CAS retry limit (100 iterations) with exponential backoff after 10
  retries to prevent indefinite spinning under extreme contention
- Comprehensive documentation of contention handling behavior

Code Quality:
- Updated deprecated // +build race to modern //go:build race format
- Added clear comments explaining memory safety concerns

CI/CD:
- Added GitHub Actions workflow for automated testing
- Workflow runs race detector on all tests automatically
- Tests run on Ubuntu, Windows, and macOS
- Extended race detector tests with 10-minute timeout

All tests pass. Thread-safety verified with concurrent tests.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

@shaia shaia requested a review from Copilot November 1, 2025 17:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go Outdated
Comment thread THREAD_SAFETY_ANALYSIS.md Outdated
CRITICAL BUG FIX: The defer statement was inside the batch operation loops,
causing pool objects to accumulate without being returned until the function
exits. For batches of 1000 items, this exhausted the pool and caused massive
memory allocation, leading to timeouts under race detector.

Changes:
- Remove defer from inside loops in AddBatch, AddBatchString, AddBatchUint64
- Call PutOperationStorage() immediately after use instead
- Ensures pool objects are returned after each iteration, not at function end

Impact:
- Fixes race detector timeout (tests now complete in <1s instead of 90s+)
- Prevents pool exhaustion on large batches
- Reduces memory pressure significantly
- Maintains proper cleanup semantics

Root Cause:
Defer statements accumulate when inside loops - all deferred calls execute
only when the function returns, not after each loop iteration. This caused
1000 pooled objects to be held simultaneously instead of being reused.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

Removed the old thread safety analysis document as the implementation
has been finalized with sync.Pool and all fixes are documented in
THREAD_SAFETY_FIXES.md.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

**Problem**: AddBatchString and AddBatchUint64 were getting/putting pool
storage for EACH item in the batch (1000 gets/puts for 1000 items),
causing excessive pool contention and allocation overhead.

**Root Cause**: Each batch function called GetOperationStorage inside the
loop, processing one item, then returning to pool before the next item.

**Solution**:
1. Get OperationStorage ONCE before the loop
2. Reuse the same ops object across all items in batch
3. Call ops.Clear() between items to reset state
4. Return to pool ONCE after all items processed (via defer)

**Changes**:
- bloomfilter.go: AddBatchString, AddBatchUint64 now get/put ops outside loop
- internal/storage/storage.go: Export Clear() method (was private clear())

**Benefits**:
- Batch of 1000 items: 2 pool operations instead of 2000 (1000x reduction)
- Reduced pool contention in concurrent scenarios
- Better cache locality (same memory reused)
- Zero change to external API or behavior

**Testing**: All integration tests pass, including concurrent operations.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

@shaia shaia requested a review from Copilot November 1, 2025 17:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go
Comment on lines +187 to +193
var positions []uint64
if bf.hashCount <= 8 {
var stackBuf [8]uint64
positions = stackBuf[:bf.hashCount]
} else {
positions = make([]uint64, bf.hashCount)
}
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to Comment 2, the stack buffer in batch operations will escape to heap if the positions slice outlives the current stack frame. Since positions is reused across loop iterations and may be passed to other functions, verify with escape analysis (go build -gcflags='-m') whether stack allocation is actually achieved.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread bloomfilter.go Outdated
Comment on lines +56 to +58
clear(os.MapOps)
clear(os.MapOpsSet)
clear(os.MapMap)
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clear() built-in for maps only zeroes entries but doesn't release underlying hash table memory. For pooled objects that may grow large, consider recreating the maps with initial capacity instead: os.MapOps = make(map[uint64][]OpDetail, 32) to avoid retaining excess memory in the pool.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,366 @@
//go:build race
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The build constraint //go:build race means these tests only run when the race detector is enabled. Consider adding a comment explaining this, e.g., // These tests only run with: go test -race to help developers understand why they might not see these tests in normal test runs.

Copilot uses AI. Check for mistakes.
strategy:
matrix:
go-version: ['1.23.x']
os: [ubuntu-latest, windows-latest, macos-latest]
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The os matrix variable is defined but the job is hardcoded to run on ubuntu-latest (line 80: runs-on: ubuntu-latest). Change line 80 to runs-on: ${{ matrix.os }} to actually test on all three platforms.

Copilot uses AI. Check for mistakes.
// PutOperationStorage returns an operation storage to the pool after clearing it
func PutOperationStorage(ops *OperationStorage) {
// Clear before returning to pool to ensure next Get receives clean object
ops.Clear()
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Clearing the storage before returning to pool may be unnecessary overhead if the next Get will immediately clear it again. However, the comment on line 222 states 'Objects from pool are already clean', so this clearing ensures the promise. Consider benchmarking whether clearing on Put vs Get has meaningful performance impact.

Copilot uses AI. Check for mistakes.
shaia added 2 commits November 1, 2025 19:41
… detector timeout

**Critical Issue**: Race detector tests were timing out (80s+) because batch
functions were calling setBitCacheOptimized(), which was getting ANOTHER ops
from the pool for every single item. For 1000 items = 1000 nested pool gets.

**Root Cause**:
- AddBatchString/AddBatchUint64 get ops once (good)
- For each item: call setBitCacheOptimized(positions)
- setBitCacheOptimized() gets its own ops from pool (BAD - nested!)
- Under race detector (10x overhead), this caused catastrophic slowdown

**Solution**:
1. Created setBitCacheOptimizedWithOps(positions, ops) internal function
2. Accepts optional pre-allocated ops to avoid pool operations
3. Batch functions now pass their ops to this internal function
4. Single pool get/put for entire batch instead of N nested operations

**Changes**:
- bloomfilter.go:
  - Added setBitCacheOptimizedWithOps(positions, ops) internal function
  - setBitCacheOptimized(positions) now delegates to internal function with nil
  - Batch functions call setBitCacheOptimizedWithOps(positions, ops)

**Performance Impact**:
- Before: 1000 items = 1 outer pool op + 1000 inner pool ops = 1001 total
- After: 1000 items = 1 pool op total
- Race detector: Tests complete in <10s instead of timing out at 80s+
- Normal mode: Concurrent writes 12M ops/sec (was 16M, minor regression acceptable)

**Testing**: All integration tests pass in 50s (including 10M element stress tests).
**Changes**:
- Removed separate "Race Detector (Extended)" job
- Merged race detector configuration into main test job
- Kept the extended timeout (10m) and GORACE environment variables
- Kept race log upload on failure for debugging

**Benefits**:
- Single comprehensive test job instead of redundant parallel jobs
- Faster CI feedback (one job instead of two doing the same thing)
- Same coverage: still runs all tests with race detector
- Still uploads detailed logs on failure for debugging

**Configuration retained**:
- go test -race -timeout=10m -v ./...
- GORACE="halt_on_error=1 log_path=/tmp/race"
- Upload race logs on failure
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

@shaia shaia requested a review from Copilot November 1, 2025 17:47
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go Outdated
Comment thread bloomfilter.go Outdated
…ocumentation

- ci: fix workflow to use matrix.os variable for cross-platform builds
- ci: remove feature branches from push trigger to avoid redundant runs
- refactor: modernize unsafe string-to-byte conversion using Go 1.20+ stdlib (unsafe.StringData/Slice)
- docs: fix misleading stack allocation comments based on escape analysis verification
- docs: document empty spin loop rationale with performance trade-offs

Performance improvements:
- Empty loop backoff maintains 18M ops/sec (vs 1.2M with runtime.Gosched)
- Stack allocation comments now accurate per escape analysis
- Modern unsafe APIs provide same zero-copy performance with clearer intent

Testing:
- All tests pass
- TestConcurrentWrites: 18.3M writes/sec
- TestMixedConcurrentOperations: 15.3M ops/sec
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

Critical bug fix: AddBatch was missing the nested pool operation fix that was
applied to AddBatchString and AddBatchUint64 in commit a0bd1a2. This caused
massive performance degradation under race detector (1,231 reads/sec vs 10M+).

Root cause: A linter or merge reverted the AddBatch fixes while preserving
the other batch function fixes.

Changes:
- Move GetOperationStorage() call outside the loop
- Use defer for pool cleanup
- Call setBitCacheOptimizedWithOps() passing ops to avoid nested pool get
- Add ops.Clear() call at end of loop for reuse

This matches the pattern established in AddBatchString and AddBatchUint64
and eliminates the nested pool operations that cause race detector timeouts.

Testing:
- All tests pass
- GitHub Actions CI should now pass with normal performance
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

The race detector adds 5-10x overhead to sync operations, causing the
concurrent tests to timeout at 93+ seconds with the full workload.

Changes:
- Add testing.Short() detection to concurrent tests
- Scale down from 100K ops to 1K ops when -short flag is used
- Update CI workflow to use -short flag with race detector
- Reduces TestConcurrentReads: 100 goroutines × 1000 ops → 10 × 100 ops
- Reduces TestConcurrentWrites: 50 goroutines × 1000 ops → 10 × 100 ops

This maintains full race detection coverage while completing in <1 second
instead of timing out. Normal tests (without -race) still run full workload
to verify high-concurrency performance.

Testing:
- go test -short ./... : passes in <1s
- go test ./... : passes with full workload, 23M+ ops/sec
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

Critical fix for GitHub Actions race detector timeout:
- Scale down pre-population in TestConcurrentReads: 10K to 1K elements
- Scale down pre-population in TestMixedConcurrentOperations: 5K to 500 elements
- Tests now complete in <1s instead of timing out (>13s)

The race detector adds 5-10x overhead to every operation. Pre-populating
10,000 elements was taking 12+ seconds on GitHub Actions runners, leaving
the test unable to complete within timeout windows.

Documentation updates:
- Enable workflow on thread-safety/* branches for testing before merge
- Update README.md with thread-safety and batch operation features
- Update CHANGELOG.md with comprehensive thread-safety documentation
- Update TESTING.md with race detector and CI/CD integration details

Root Cause:
- GitHub Actions: Shared CPU, cold cache, race detector overhead
- Local machine: Fast dedicated CPU, warm cache, same overhead but faster
- Pre-population was the bottleneck, not the concurrent tests themselves
Added three comprehensive examples:
1. Basic Usage - Single element operations
2. Batch Operations - AddBatchString and AddBatchUint64 for high throughput
3. Thread-Safe Concurrent Operations - 10 goroutines writing concurrently

Demonstrates the new thread-safety and batch operation features
added in this release.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

…functions

Applied the same defensive pattern used in getHashPositionsOptimized to all
three AddBatch functions (AddBatch, AddBatchString, AddBatchUint64).

While the current code is safe (ops remains valid until defer at function exit),
this change ensures consistency across the codebase and protects against future
refactoring issues.

Changes:
- Copy cache line indices slice before use in all AddBatch functions
- Matches the defensive pattern in getHashPositionsOptimized (lines 413-415)
- Adds explanatory comment about pooled storage safety
- Zero performance impact: allocation is negligible vs batch operation cost

All tests pass with performance still at 18M+ writes/sec.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

Fixed GitHub Actions test failures caused by excessive race detector overhead
and incorrect test verification logic.

Issues fixed:
1. Race detector overhead on GitHub Actions runners was too high
   - Reduced workload by 50-80% when running with -short flag
   - TestConcurrentReads: 500 elements (was 1000), 5 goroutines (was 10)
   - TestConcurrentWrites: 5 goroutines (was 10), 50 writes (was 100)
   - TestMixedConcurrentOperations: 250 elements (was 500), 5/5 readers/writers (was 10/10)

2. Test verification bug in TestConcurrentWrites
   - Was verifying sampleSize/numGoroutines keys per goroutine (200 keys)
   - But only numWritesPerGoroutine keys were written (50 keys)
   - Fixed to verify min(numWritesPerGoroutine, 100) keys per goroutine
   - Now correctly validates all written keys without false failures

All tests pass locally and should now pass on GitHub Actions with race detector.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

The test job was hardcoded to ubuntu-latest despite having a matrix
strategy defined. This commit fixes the workflow to actually run tests
on all three platforms (Ubuntu, Windows, macOS).

Changes:
- Use ${{ matrix.os }} instead of hardcoded ubuntu-latest
- Add os matrix variable [ubuntu-latest, windows-latest, macos-latest]
- Update race detector log path to be cross-platform compatible
- Add platform suffix to race log artifact names to avoid conflicts

This ensures comprehensive testing across all supported platforms and
catches platform-specific issues early in CI/CD.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

Previously both 'test' and 'build' jobs ran on all platforms, causing
unnecessary duplication. This commit optimizes the workflow:

- test job: Runs comprehensive tests with race detector on Ubuntu only
  (fastest runner, sufficient for correctness validation)
- build job: Validates builds work on all platforms (Ubuntu, Windows, macOS)

Benefits:
- Faster CI runs (3 test jobs → 1 test job + 3 build jobs)
- Lower compute costs while maintaining full platform coverage
- Race detector still runs on every push/PR
- Platform-specific build issues still caught by build job

This provides the same level of quality assurance with better efficiency.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

Implemented smart hybrid CI/CD trigger strategy to avoid redundant runs:

Before:
- Push to thread-safety/* branches → triggers workflow
- Create PR from thread-safety/* → triggers workflow again (duplicate)
- Result: Same commits tested twice

After:
- Push to main branch only → validates production
- Pull requests to main → validates changes before merge
- Push to feature branches → no workflow (waits for PR)
- Result: Each commit tested once, no duplicates

Benefits:
- Eliminates duplicate CI runs for PR branches
- Maintains protection for main branch (push events)
- Maintains PR validation (PR status checks in GitHub UI)
- Reduces CI compute costs by ~50% for typical workflow
- Follows best practices for GitHub Actions efficiency

Feature branches are now validated only when a PR is created,
preventing the "double run" scenario while ensuring all code
reaching main is properly tested.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 1, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 2, 2025

📋 Version Check: This PR contains changes. Consider creating a new version tag after merging.

Semantic Versioning Guide:

  • vX.Y.Z - Patch: Bug fixes, performance improvements
  • vX.Y.0 - Minor: New features, backward compatible
  • vX.0.0 - Major: Breaking changes

To create a release after merging:

git tag v0.1.0
git push origin v0.1.0

Copy link
Copy Markdown

Copilot AI commented Nov 2, 2025

@shaia I've opened a new pull request, #18, to work on those changes. Once the pull request is ready, I'll request review from you.

@shaia shaia merged commit c291acb into main Nov 2, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants