-
Notifications
You must be signed in to change notification settings - Fork 28
[Add] semaphore for blocking aerospike queries #813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
3e20843
334cd42
289667c
c9ce483
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -537,6 +537,118 @@ func TestClient_AcquirePermitTimeout(t *testing.T) { | |
| }) | ||
| } | ||
|
|
||
| func TestEnableQuerySemaphore(t *testing.T) { | ||
| t.Run("default size is 25% of conn pool", func(t *testing.T) { | ||
| client := &Client{ | ||
| connSemaphore: make(chan struct{}, 128), | ||
| stats: NewClientStats(), | ||
| } | ||
| assert.Equal(t, 0, client.GetQuerySemaphoreSize()) | ||
|
|
||
| client.EnableQuerySemaphore(0) // 0 means use default fraction | ||
| assert.Equal(t, 32, client.GetQuerySemaphoreSize()) // 25% of 128 | ||
| }) | ||
|
|
||
| t.Run("explicit size", func(t *testing.T) { | ||
| client := &Client{ | ||
| connSemaphore: make(chan struct{}, 256), | ||
| stats: NewClientStats(), | ||
| } | ||
| client.EnableQuerySemaphore(16) | ||
| assert.Equal(t, 16, client.GetQuerySemaphoreSize()) | ||
| }) | ||
|
|
||
| t.Run("conn semaphore unchanged", func(t *testing.T) { | ||
| client := &Client{ | ||
| connSemaphore: make(chan struct{}, 256), | ||
| stats: NewClientStats(), | ||
| } | ||
| client.EnableQuerySemaphore(8) | ||
| assert.Equal(t, 256, client.GetConnectionQueueSize()) // unchanged | ||
| assert.Equal(t, 8, client.GetQuerySemaphoreSize()) | ||
| }) | ||
|
|
||
| t.Run("small pool gets minimum 1", func(t *testing.T) { | ||
| client := &Client{ | ||
| connSemaphore: make(chan struct{}, 2), | ||
| stats: NewClientStats(), | ||
| } | ||
| client.EnableQuerySemaphore(0) | ||
| assert.Equal(t, 1, client.GetQuerySemaphoreSize()) | ||
| }) | ||
| } | ||
|
|
||
| func TestExtractTimeout(t *testing.T) { | ||
| t.Run("nil policy", func(t *testing.T) { | ||
| assert.Equal(t, time.Duration(0), extractTimeout(nil)) | ||
| }) | ||
|
|
||
| t.Run("BasePolicy with timeout", func(t *testing.T) { | ||
| p := &aerospike.BasePolicy{TotalTimeout: 5 * time.Second} | ||
| assert.Equal(t, 5*time.Second, extractTimeout(p)) | ||
| }) | ||
|
|
||
| t.Run("WritePolicy with timeout", func(t *testing.T) { | ||
| p := aerospike.NewWritePolicy(0, 0) | ||
| p.TotalTimeout = 3 * time.Second | ||
| assert.Equal(t, 3*time.Second, extractTimeout(p)) | ||
| }) | ||
|
|
||
| t.Run("BatchPolicy with timeout", func(t *testing.T) { | ||
| p := aerospike.NewBatchPolicy() | ||
| p.TotalTimeout = 2 * time.Second | ||
| assert.Equal(t, 2*time.Second, extractTimeout(p)) | ||
| }) | ||
|
|
||
| t.Run("QueryPolicy with timeout", func(t *testing.T) { | ||
| p := aerospike.NewQueryPolicy() | ||
| p.TotalTimeout = 10 * time.Second | ||
| assert.Equal(t, 10*time.Second, extractTimeout(p)) | ||
| }) | ||
|
|
||
| t.Run("unknown policy type", func(t *testing.T) { | ||
| assert.Equal(t, time.Duration(0), extractTimeout("not-a-policy")) | ||
| }) | ||
| } | ||
|
|
||
| func TestClient_QuerySemaphoreTimeout(t *testing.T) { | ||
| t.Run("query semaphore timeout with QueryPolicy", func(t *testing.T) { | ||
| client := &Client{ | ||
| connSemaphore: make(chan struct{}, 1), | ||
| stats: NewClientStats(), | ||
| } | ||
| client.EnableQuerySemaphore(1) | ||
|
|
||
| // Fill the query semaphore | ||
| client.querySemaphore <- struct{}{} | ||
|
|
||
| policy := aerospike.NewQueryPolicy() | ||
| policy.TotalTimeout = 1000 * time.Millisecond | ||
|
|
||
| start := time.Now() | ||
| err := client.acquireQueryPermit(policy) | ||
| elapsed := time.Since(start) | ||
|
|
||
| assert.Error(t, err) | ||
| assert.True(t, elapsed >= minSemaphoreTimeout && elapsed < 200*time.Millisecond, | ||
| "Expected timeout around %v, got %v", minSemaphoreTimeout, elapsed) | ||
| }) | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Minor] Missing test coverage for goroutine lifecycle ✅ FIXED The current test suite now includes comprehensive goroutine lifecycle tests:
This directly tests the early-termination scenario that was missing before. Issue resolved. |
||
|
|
||
| func TestGetConnectionQueueSize_OnlyConnSemaphore(t *testing.T) { | ||
| client := &Client{ | ||
| connSemaphore: make(chan struct{}, 128), | ||
| stats: NewClientStats(), | ||
| } | ||
| assert.Equal(t, 128, client.GetConnectionQueueSize()) | ||
| assert.Equal(t, 0, client.GetQuerySemaphoreSize()) | ||
|
|
||
| // After enabling query semaphore, conn size is unchanged | ||
| client.EnableQuerySemaphore(16) | ||
| assert.Equal(t, 128, client.GetConnectionQueueSize()) | ||
| assert.Equal(t, 16, client.GetQuerySemaphoreSize()) | ||
| } | ||
|
|
||
| // Test mock functionality separately | ||
| func TestMockAerospikeClient_CompleteCoverage(t *testing.T) { | ||
| t.Run("mock client functionality", func(t *testing.T) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Major] Shared client mutation — multiple service instances may conflict ✅ FIXED
Current implementation (lines 354-366):
EnableQuerySemaphorenow usesCompareAndSwapto make initialization idempotent. Only the first caller wins; subsequent calls are safely ignored. Documentation explicitly states: "EnableQuerySemaphore is idempotent: subsequent calls are ignored... It is safe to call concurrently with QueryPartitions/Query."Test coverage:
TestEnableQuerySemaphore_Idempotent: Verifies second call with different size does not change the channelTestEnableQuerySemaphore_ConcurrentEnable: 32 goroutines race to enable; exactly one channel installedIssue resolved.