-
Notifications
You must be signed in to change notification settings - Fork 862
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[azservicebus] Fixing a memory leak when doing cross receiver settlem…
…ent. (#22368) Fixing a memory leak when doing cross receiver settlement. go-amqp holds onto some tracking data that won't get cleared if we don't try to settle through the original Receiver. Our fix in here, combined with go-amqp's changes to route to the original receiver, should seal that up. Benchmark added that also doubles as a stress test. Fixes #22318
- Loading branch information
1 parent
e62d2c1
commit 7899ebe
Showing
21 changed files
with
470 additions
and
160 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
dependencies: | ||
- name: stress-test-addons | ||
repository: https://stresstestcharts.blob.core.windows.net/helm/ | ||
version: 0.3.0 | ||
digest: sha256:3e21a7fdf5d6b37e871a6dd9f755888166fbb24802aa517f51d1d9223b47656e | ||
generated: "2023-09-26T11:43:56.706771668-07:00" | ||
version: 0.3.1 | ||
digest: sha256:28e374f8db5c46447b2a1491d4361ceb126536c425cbe54be49017120fe7b27d | ||
generated: "2024-02-05T17:21:31.510400504-08:00" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
sdk/messaging/azservicebus/internal/stress/tests/benchmarks/backup_settlement_leak_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
package benchmarks | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"math" | ||
"runtime" | ||
"sync" | ||
"sync/atomic" | ||
"testing" | ||
"time" | ||
|
||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to" | ||
"github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus" | ||
"github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus/admin" | ||
"github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus/internal/stress/shared" | ||
) | ||
|
||
// BackupSettlementLeak checks that, when we use backup settlement, that we're not | ||
// leaking memory. This came up in a couple of issues for a customer: | ||
// - https://github.com/Azure/azure-sdk-for-go/issues/22318 | ||
// - https://github.com/Azure/azure-sdk-for-go/issues/22157 | ||
// | ||
// The use case for backup settlement is for when the original link we've received | ||
// on has gone offline, so we need to settle via the management$ link instead. However, | ||
// the underlying go-amqp link is tracking several bits of state for the message which | ||
// will never get cleared. Since that receiver was dead it was going to get garbage | ||
// collected anyways, so this was non-issue. | ||
// | ||
// This customer's use case was slightly different - they were completing on a separate | ||
// receiver even when the original receiving link was still alive. This means the memory | ||
// leak is just accumulating and never gets garbage collected since there's no trigger | ||
// to know when to clear out any tracking state for the message. | ||
func BenchmarkBackupSettlementLeakWhileOldReceiverStillAlive(b *testing.B) { | ||
b.StopTimer() | ||
|
||
sc := shared.MustCreateStressContext("BenchmarkBackupSettlementLeak", nil) | ||
defer sc.End() | ||
|
||
sent := int64(100000) | ||
|
||
client, queueName := mustInitBenchmarkBackupSettlementLeak(sc, b, int(sent)) | ||
|
||
oldReceiver, err := client.NewReceiverForQueue(queueName, nil) | ||
sc.NoError(err) | ||
|
||
newReceiver, err := client.NewReceiverForQueue(queueName, nil) | ||
sc.NoError(err) | ||
|
||
b.StartTimer() | ||
|
||
var completed int64 | ||
expected := maxDeliveryCount * int64(sent) | ||
|
||
for completed < expected { | ||
// receive from the old receiver and... | ||
receiveCtx, cancel := context.WithTimeout(context.Background(), time.Minute) | ||
|
||
messages, err := oldReceiver.ReceiveMessages(receiveCtx, int(math.Min(float64(expected-completed), 5000)), &azservicebus.ReceiveMessagesOptions{ | ||
// not super scientific - mostly just want to get slightly fuller batches | ||
TimeAfterFirstMessage: 30 * time.Second, | ||
}) | ||
cancel() | ||
sc.NoError(err) | ||
|
||
wg := sync.WaitGroup{} | ||
wg.Add(len(messages)) | ||
|
||
// ...completing on another receiver | ||
for _, m := range messages { | ||
m := m | ||
|
||
go func() { | ||
defer wg.Done() | ||
|
||
// abandon it so we see the message a few times (until it's deadlettered after 10 tries) | ||
err := newReceiver.AbandonMessage(context.Background(), m, nil) | ||
sc.NoError(err) | ||
atomic.AddInt64(&completed, 1) | ||
}() | ||
} | ||
|
||
wg.Wait() | ||
|
||
b.Logf("Settled %d/%d", completed, sent) | ||
} | ||
|
||
log.Printf("Forcing garbage collection\n") | ||
runtime.GC() | ||
log.Printf("Done with collection\n") | ||
time.Sleep(1 * time.Minute) | ||
} | ||
|
||
func mustInitBenchmarkBackupSettlementLeak(sc *shared.StressContext, b *testing.B, numToSend int) (*azservicebus.Client, string) { | ||
queueName := fmt.Sprintf("backup-settlement-tester-%s", sc.Nano) | ||
shared.MustCreateAutoDeletingQueue(sc, queueName, &admin.QueueProperties{ | ||
MaxDeliveryCount: to.Ptr[int32](maxDeliveryCount), | ||
}) | ||
|
||
client, err := azservicebus.NewClientFromConnectionString(sc.ConnectionString, nil) | ||
sc.PanicOnError("failed to create client", err) | ||
|
||
sender, err := shared.NewTrackingSender(sc.TC, client, queueName, nil) | ||
sc.PanicOnError("create a sender", err) | ||
|
||
shared.MustGenerateMessages(sc, sender, numToSend, 0) | ||
|
||
return client, queueName | ||
} | ||
|
||
const maxDeliveryCount = 20 |
27 changes: 27 additions & 0 deletions
27
sdk/messaging/azservicebus/internal/stress/tests/benchmarks/main_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
package benchmarks | ||
|
||
import ( | ||
"log" | ||
"os" | ||
"testing" | ||
|
||
"github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus/internal/stress/shared" | ||
) | ||
|
||
func TestMain(m *testing.M) { | ||
if os.Getenv("ENV_FILE") == "" { | ||
os.Setenv("ENV_FILE", "../../../../.env") | ||
} | ||
|
||
err := shared.LoadEnvironment() | ||
|
||
if err != nil { | ||
log.Printf("Failed to load env file, benchmarks will not run: %s", err) | ||
return | ||
} | ||
|
||
os.Exit(m.Run()) | ||
} |
9 changes: 9 additions & 0 deletions
9
sdk/messaging/azservicebus/internal/stress/tests/benchmarks/readme.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
## Generating | ||
|
||
`go test -memprofile mem.out -bench .` | ||
|
||
## Visualizing | ||
|
||
Run: | ||
* `sudo apt install graphviz` | ||
* `go tool pprof -http localhost:8000 -base mem.out.before_fix mem.out.after_fix` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.