Repository navigation
Conversation
Tessera's storage drivers open database connections and never close them: the AWS driver's MySQL pool, the Spanner client the GCP driver creates when none is passed in, and the pools, clients and databases of the AWS, GCP and Badger antispams. A process that opens a log more than once keeps every one of them, and Badger keeps its directory lock, so its database can't be opened again in that process. Construction that fails part way leaks what it opened too. Each appender now closes what it opened once its context is done and its background jobs have returned, following the documented shutdown: call the shutdown function, then cancel the context. The AWS driver closes its MySQL pool. The GCP driver closes a Spanner client it created, but not one passed in Config.SpannerClient, and no longer keeps the client it creates in its Config, so that other lifecycles from the same driver don't share a client it closes. The GCS client is left open, as its Close makes any later use panic. Construction that fails closes what it opened. The antispams are created by the caller, so each gets a Close method. The GCP antispam closes only a client it created. The Badger antispam stops its compaction, then closes its database, releasing the lock. There is no way yet to wait for the cleanup: it happens shortly after the context ends.
This branch has not been deployed
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1251.
Part of #1111: Tessera now closes what it opens, but there's no way yet to wait for that cleanup (see "Not included").
This replaces #1252, which let callers pass in their own
*sql.DB. As @mhutchinson pointed out there, the root bug is better fixed by Tessera closing what it opens, which also covers everyone who passes a DSN.Why
We found this through our own log's stress testing. Its burn-in restarts the log in-process, and its tests and fuzzers open many logs in one process. Tessera never closed the MySQL pools it opened, so they added up to more connections than MySQL allows by default, and we had to cap the burn-in's restarts and raise
max_connections. The same pattern turned out to be in all three drivers:Construction that failed part way leaked what it had opened too, as in my comment on #1251.
What changes
sync.WaitGroup, and a goroutine closes the pool or client once they've returned.Config.SpannerClientis left open. The appender no longer stores the client it creates in itsConfig, so the driver's other lifecycles don't share a client it closes.Closemakes any later use panic, and it holds only idle HTTP connections.newMySQLSequencerand the AWSNewAntispamclose their pool whenPing,initDBorcheckDataCompatibilityfails.newAppenderclose the sequencer when they fail.Appendercloses a client it created wheninitDBornewSpannerCoordinatorfails.Close() error, since the caller creates it.Closetwice is safe.Behaviour changes
LogReader'sIntegratedSizeandNextIndexreturn errors on AWS and GCP, since they read the closed pool or client. Reads of checkpoints, tiles and bundles are unaffected.Closeafter shutting the appender down and cancelling its context. One that doesn't is no worse off than before.Not included
Testing
spannertestfor GCP, temporary directories for POSIX). Each fails without its fix:TestMySQLSequencerClosesPoolOnFailureandTestAppenderClosesSequencer(AWS);TestNewAntispamClosesPoolOnFailureandTestAntispamClose(AWS antispam);TestAppenderClosesSpannerClient(GCP: its own client, when the context ends and whennewAppenderfails; a caller's client stays open);TestAntispamClose(GCP antispam: its own client closes; a caller's stays open);TestAntispamClose(Badger: open, close and reopen the same directory three times, with all goroutines gone).go test -race ./...passes on macOS, apart fromTestConstructAndUseAppender(/tmpis a symlink) andTestPosixMirrorIntegration(TestPosixMirrorIntegration fails on macOS: its burst of connections overflows the listen queue #1253, fixed by integration/mirror: limit the test's connections #1254), which fail the same way onmainhere.go test -p=1 -race ./storage/aws/... -is_mysql_test_optional=falsepasses against MySQL 8.4.golangci-lintv2.13.2 reports 0 issues.-race, with 8 concurrent adders, re-adds of earlier entries, and every third round cancelled mid-flight without calling shutdown.spannertest; POSIX with the Badger antispam.main: the same runs fail at the second reopen. AWS leaks 9 to 13 MySQL connections a reopen and hits the connection limit by the fourth, GCP leaks about 50 goroutines a reopen on the emulator, and Badger can't reopen its directory.main, AWS leaked 28 MySQL connections, GCP 2,000 goroutines, and Badger couldn't reopen at all.Closetwice, an add afterClose,Closewhile the follower runs, and theLogReaderafter the context ends all return errors, with no panics or races.This touches
storage/aws/aws.go, as #1257 does, and the two merge cleanly in either order. #1252 conflicts with it, and I'll close #1252.Reviewed and submitted by me, a human. The investigation, the fix and its testing were developed with the assistance of an AI coding agent.