Skip to content

Refactor concurrent tests with explicit goroutine orchestration#381

Merged
gaissmai merged 1 commit into
mainfrom
devel
May 31, 2026
Merged

Refactor concurrent tests with explicit goroutine orchestration#381
gaissmai merged 1 commit into
mainfrom
devel

Conversation

@gaissmai
Copy link
Copy Markdown
Owner

@gaissmai gaissmai commented May 31, 2026

Summary by CodeRabbit

  • Tests
    • Improved concurrent test examples: readers now run concurrently with two separate writer streams (inserts and deletes) to more aggressively exercise concurrent access.
    • Added coordination so all goroutines start together and the reader stops when writers finish, improving reliability and race detection.
    • Introduced a new internal test payload type with cloning support for safer value handling in tests.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5c09c1ff-1d42-4e75-89f0-da947fb84ea1

📥 Commits

Reviewing files that changed from the base of the PR and between 4fe3576 and 8d92c6d.

📒 Files selected for processing (4)
  • example_fast_concurrent_test.go
  • example_lite_concurrent_test.go
  • example_table_concurrent_test.go
  • example_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • example_lite_concurrent_test.go
  • example_fast_concurrent_test.go

📝 Walkthrough

Walkthrough

Three concurrent test examples refactor from simple WaitGroup coordination to explicit goroutine orchestration using separate reader/writer wait groups, a startSignal channel, and a writersDone channel for deterministic synchronization. Each example now spawns a dedicated reader performing atomic Contains checks, two distinct writer goroutines (one for inserts, one for deletes), and an orchestration goroutine managing shutdown. Copyright year updated to 2026.

Changes

Concurrent Test Orchestration Refactoring

Layer / File(s) Summary
Reader and insert writer orchestration
example_fast_concurrent_test.go, example_lite_concurrent_test.go, example_table_concurrent_test.go
Reader goroutine setup with atomic loads and Contains checks until writersDone closes, plus first writer goroutine for batched InsertPersist updates under mutex protection, shared across all three examples. Copyright year updated to 2026. For Table, globalSink variable introduced to inhibit compiler optimization of reader results.
Delete writer and shutdown orchestration
example_fast_concurrent_test.go, example_lite_concurrent_test.go, example_table_concurrent_test.go
Second writer goroutine for batched DeletePersist updates, plus dedicated orchestration goroutine that waits for all writers, closes writersDone to signal reader shutdown, then returns from the test with all goroutines cleanly joined.

Sequence Diagram(s)

sequenceDiagram
  participant Main as main
  participant Start as startSignal (closed by main)
  participant InsertWriter as InsertWriter
  participant DeleteWriter as DeleteWriter
  participant Reader as ReaderLoop
  participant Table as AtomicTable

  Main->>Start: close(startSignal)
  Start->>InsertWriter: begin inserts
  Start->>DeleteWriter: begin deletes
  InsertWriter->>Table: load -> Apply InsertPersist under tblMutex -> atomic.Store(updated)
  DeleteWriter->>Table: load -> Apply DeletePersist under tblMutex -> atomic.Store(updated)
  Reader->>Table: atomic.Load -> Contains checks (loop)
  InsertWriter->>Main: wg.Done
  DeleteWriter->>Main: wg.Done
  Main->>Reader: close(writersDone) -> reader exits
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • gaissmai/bart#299: Related changes to concurrent example coordination and atomic/mutex usage in example tests.
  • gaissmai/bart#326: Prior updates to concurrent example tests and atomic publish patterns that overlap with these changes.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title Check ✅ Passed Title check skipped as CodeRabbit has written the PR title.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch devel

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot changed the title @coderabbitai Refactor concurrent tests with explicit goroutine orchestration May 31, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 31, 2026

Greptile Summary

This PR refactors three concurrent example tests to use explicit goroutine orchestration: a shared startSignal channel releases all goroutines simultaneously, and separate readerWg/writerWg wait-groups plus a writersDone channel let the reader run until both writer streams finish. The testVal type is also consolidated into example_test.go.

  • The reader loop now uses plain = assignment (localSink = ...Load().Contains(ip)), so Contains is called unconditionally on every IP on every iteration — the reader fully exercises the atomic pointer throughout the entire test run.
  • Two writer goroutines (inserts and deletes) run concurrently and independently, giving the race detector more opportunities to observe conflicting access patterns.
  • An orchestration goroutine closes writersDone only after both writers finish, ensuring the reader does not exit prematurely.

Confidence Score: 5/5

Safe to merge. The changes are confined to example/test files and the goroutine orchestration logic is correct and well-structured.

The refactored tests introduce a clean start-signal/shutdown pattern with no observable correctness issues. The reader uses plain assignment so every Contains call is always executed. Writers are properly protected by a mutex. The orchestration goroutine always runs to completion before the main goroutine can exit, so there are no leaks or premature exits.

No files require special attention.

Important Files Changed

Filename Overview
example_table_concurrent_test.go Refactored to use explicit startSignal channel and separate readerWg/writerWg; the testVal type was moved to example_test.go; the reader now uses plain assignment (not
example_fast_concurrent_test.go Same orchestration refactor as the Table variant; reader goroutine calls tblAtomicPtr.Load().Contains(ip) unconditionally via plain assignment to localSink on each iteration.
example_lite_concurrent_test.go Same orchestration refactor as the Table/Fast variants; reader uses plain assignment so liteAtomicPtr.Load().Contains(ip) is always evaluated, not short-circuited.
example_test.go Received the testVal struct and its Clone method, consolidating the shared test payload type into one place for all three concurrent example tests.

Sequence Diagram

sequenceDiagram
    participant Main
    participant Reader
    participant WriterInserts
    participant WriterDeletes
    participant Orchestration

    Main->>Reader: go func (blocked on startSignal)
    Main->>WriterInserts: go func (blocked on startSignal)
    Main->>WriterDeletes: go func (blocked on startSignal)
    Main->>Orchestration: go func (blocked on startSignal)
    Main->>Main: close(startSignal)

    par All goroutines start simultaneously
        Reader->>Reader: loop: select writersDone / default Contains
        WriterInserts->>WriterInserts: 1000 x InsertPersist (mutex-guarded)
        WriterDeletes->>WriterDeletes: 1000 x DeletePersist (mutex-guarded)
        Orchestration->>Orchestration: writerWg.Wait()
    end

    WriterInserts->>Orchestration: writerWg.Done()
    WriterDeletes->>Orchestration: writerWg.Done()
    Orchestration->>Reader: close(writersDone)
    Reader->>Main: readerWg.Done()
    Main->>Main: readerWg.Wait() returns, test ends
Loading

Reviews (6): Last reviewed commit: "refactor the three concurrent example te..." | Re-trigger Greptile

Comment thread example_table_concurrent_test.go
Comment thread example_fast_concurrent_test.go
Comment thread example_lite_concurrent_test.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
example_table_concurrent_test.go (2)

95-95: 💤 Low value

Inconsistent numeric literal style.

Line 95 uses 1000 while line 117 uses 1_000. Consider aligning these for consistency.

Also applies to: 117-117

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example_table_concurrent_test.go` at line 95, The numeric literal style is
inconsistent between the two for loops using "for range 1000" and "for range
1_000"; update both occurrences to the same style (choose either "1000" or
"1_000") so the test file example_table_concurrent_test.go uses a consistent
numeric literal format for those loops.

13-14: 💤 Low value

Consider moving globalSink to a shared test helper.

This variable is used across all three example test files (example_fast_concurrent_test.go, example_lite_concurrent_test.go, and this file). Placing it here creates an implicit dependency requiring all example files to be compiled together. A dedicated example_test_helpers.go file would make this dependency explicit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example_table_concurrent_test.go` around lines 13 - 14, The shared test
variable globalSink should be moved out of example_table_concurrent_test.go into
a dedicated test helper file (e.g., example_test_helpers.go) so all
example_*_concurrent_test.go files can import it without implicit compile-time
coupling; create the helper file, declare var globalSink bool (with the same
package and test build tag if needed), remove the declaration from
example_table_concurrent_test.go, and update other example test files to rely on
the helper's globalSink symbol.
example_fast_concurrent_test.go (2)

78-78: 💤 Low value

Inconsistent numeric literal style.

Line 78 uses 1000 while line 100 uses 1_000. For consistency across all three example files, consider using the same style (preferably 1_000 for readability).

Also applies to: 100-100

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example_fast_concurrent_test.go` at line 78, Inconsistent numeric literal
styling: replace the plain integer literal used in the loop (the "for range
1000" occurrence) with the underscore-separated form "1_000" to match the style
used elsewhere; search for other occurrences of "1000" in the example files (the
two other example_* files mentioned) and update them to "1_000" as well so all
three examples use the same readable numeric literal format.

52-56: ⚡ Quick win

globalSink is declared in the same test package, so the reader’s assignment is valid.

example_fast_concurrent_test.go and example_table_concurrent_test.go are both package bart_test, and example_table_concurrent_test.go declares var globalSink bool, making globalSink = localSink compile correctly across these test files.

Minor: numeric literal style is inconsistent (1000 vs 1_000).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example_fast_concurrent_test.go` around lines 52 - 56, The assignment
globalSink = localSink is valid because globalSink is declared in the same test
package (see globalSink and localSink usage), so do not remove or change that
assignment; instead address the minor style inconsistency by normalizing numeric
literals (e.g., change 1000 to 1_000 or vice versa) in this test file so numeric
literal formatting matches the rest of the codebase.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@example_lite_concurrent_test.go`:
- Around line 48-52: The test assigns to an undeclared identifier globalSink
inside the writer loop (using localSink and writersDone), causing a compile
error; declare a package-level variable named globalSink of type bool in this
file (or add a local variable visible to the test) so the assignment in the loop
compiles—e.g. add a var globalSink bool above the test or at file scope so the
assignment to globalSink from localSink succeeds.

---

Nitpick comments:
In `@example_fast_concurrent_test.go`:
- Line 78: Inconsistent numeric literal styling: replace the plain integer
literal used in the loop (the "for range 1000" occurrence) with the
underscore-separated form "1_000" to match the style used elsewhere; search for
other occurrences of "1000" in the example files (the two other example_* files
mentioned) and update them to "1_000" as well so all three examples use the same
readable numeric literal format.
- Around line 52-56: The assignment globalSink = localSink is valid because
globalSink is declared in the same test package (see globalSink and localSink
usage), so do not remove or change that assignment; instead address the minor
style inconsistency by normalizing numeric literals (e.g., change 1000 to 1_000
or vice versa) in this test file so numeric literal formatting matches the rest
of the codebase.

In `@example_table_concurrent_test.go`:
- Line 95: The numeric literal style is inconsistent between the two for loops
using "for range 1000" and "for range 1_000"; update both occurrences to the
same style (choose either "1000" or "1_000") so the test file
example_table_concurrent_test.go uses a consistent numeric literal format for
those loops.
- Around line 13-14: The shared test variable globalSink should be moved out of
example_table_concurrent_test.go into a dedicated test helper file (e.g.,
example_test_helpers.go) so all example_*_concurrent_test.go files can import it
without implicit compile-time coupling; create the helper file, declare var
globalSink bool (with the same package and test build tag if needed), remove the
declaration from example_table_concurrent_test.go, and update other example test
files to rely on the helper's globalSink symbol.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d2fc7b51-1a98-41ec-857a-027a60451708

📥 Commits

Reviewing files that changed from the base of the PR and between b2d7d2c and 4fe3576.

📒 Files selected for processing (3)
  • example_fast_concurrent_test.go
  • example_lite_concurrent_test.go
  • example_table_concurrent_test.go

Comment thread example_lite_concurrent_test.go Outdated
ExampleTable_concurrent, ExampleFast_concurrent, ExampleLite_concurrent now
uses explicit goroutine orchestration via a startSignal channel broadcast
and separate reader/writer wait groups, replacing the previous fixed-iteration
reader loops.

The reader goroutine now runs a non-blocking select/default busy-poll loop
continuously until all writers signal completion through the writersDone
channel, giving the race detector far more concurrent read/write overlap
to observe.

testVal and its Clone method were lifted from example_table_concurrent_test.go
into example_test.go so all concurrent example tests share the type without
duplication.
@gaissmai
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@gaissmai
Copy link
Copy Markdown
Owner Author

@greptileai help

@gaissmai gaissmai closed this May 31, 2026
@gaissmai gaissmai reopened this May 31, 2026
@gaissmai gaissmai merged commit 5595978 into main May 31, 2026
26 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.

1 participant