fix: handle concurrent updates when marking obsolete secret revisions#21779
Merged
Conversation
d32395b to
487fe1b
Compare
9ad3d5a to
32149db
Compare
32149db to
62c8e67
Compare
hpidcock
approved these changes
Feb 16, 2026
jujubot
added a commit
that referenced
this pull request
Feb 23, 2026
#21795 Merge 3.6 Bring forward dependabot changes #21751 [from anvial/36-40-transition-guide](f8067fd) #21760 [from anvial/fix-doc-sidemenu](41f8ba9) #21557 [from raineszm/fix/int-options-represented-a…](81cff4d) Drop: #21741 [from wallyworld/limit-apiport-again](627aab5) The shell tests are retained as jenkins job config is derived from main - test config allows tests to be targetted. Drop mongo work: #21788 [from wallyworld/fix-backup](236532c) #21779 [from wallyworld/robust-secret-updates](b859d96) #21782 [from manadart/3.6-log-ops-on-errors](70a3817) Also drop commits that were backported from 4.0 to 3.6 and brought forward again in the merge. The api port expose feature was dropped (needs reimplementing). But one change from that PR was re-implemented - we don't want to allow the controller app to be unexposed. ``` # Conflicts: # .github/workflows/upgrade.yml # agent/errors/errors.go # api/agent/instancemutater/mocks/caller_mock.go # api/agent/secretsdrain/mocks/facade_mock.go # api/agent/uniter/resource.go # api/agent/uniter/resource_test.go # api/apiclient.go # api/apiclient_test.go # api/apiclient_whitebox_test.go # api/base/mocks/caller_mock.go # api/base/mocks/clientfacade_mock.go # api/client/charms/downloader.go # api/client/charms/downloader_test.go # api/client/client/client.go # api/client/modelupgrader/mocks/apibase_mock.go # api/client/modelupgrader/package_test.go # api/client/modelupgrader/upgrader.go # api/client/modelupgrader/upgrader_test.go # api/client/resources/client.go # api/client/resources/client_upload_test.go # api/connection.go # api/controller/crossmodelrelations/crossmodelrelations.go # api/controller/crossmodelsecrets/crossmodelsecrets.go # api/controller/migrationtarget/client.go # api/controller/usersecretsdrain/mocks/facade_mock.go # api/http/http.go # apiserver/apiserver.go # apiserver/apiserver_test.go # apiserver/facades/client/application/application.go # apiserver/facades/client/application/application_test.go # apiserver/facades/controller/firewaller/firewaller.go # apiserver/facades/controller/firewaller/firewaller_unit_test.go # apiserver/httpattachment/attachment.go # apiserver/registration_proxy_mock_test.go # cmd/juju/application/bundle/bundle_test.go # cmd/juju/application/deployer/mocks/deploy_mock.go # cmd/juju/commands/bootstrap_test.go # cmd/juju/common/controller.go # cmd/juju/common/controller_test.go # cmd/jujud/agent/bootstrap.go # cmd/jujud/agent/bootstrap_test.go # cmd/jujud/agent/controllercharm.go # cmd/modelcmd/mocks/api_mock.go # environs/jujutest/livetests.go # go.mod # go.sum # internal/container/broker/instance_broker.go # internal/provider/common/bootstrap.go # internal/provider/dummy/environs.go # internal/provider/ec2/local_test.go # internal/provider/kubernetes/specs/builder.go # internal/proxy/proxy.go # internal/proxy/testing/proxy.go # internal/worker/caasapplicationprovisioner/application.go # internal/worker/caasapplicationprovisioner/application_test.go # internal/worker/caasapplicationprovisioner/mocks/runner_mock.go # internal/worker/caasapplicationprovisioner/worker.go # internal/worker/caasfirewallersidecar/mocks/api_base_mock.go # internal/worker/caasoperator/mocks/apibase.go # internal/worker/containerbroker/mocks/base_mock.go # internal/worker/firewaller/firewaller.go # internal/worker/firewaller/firewaller_test.go # internal/worker/instancemutater/mocks/base_mock.go # rpc/client.go # rpc/jsoncodec/codec.go # rpc/jsoncodec/codec_test.go # rpc/jsoncodec/conn.go # rpc/rpc_test.go # rpc/server.go # state/application_ports.go # state/backups/db.go # state/backups/db_dump_test.go # state/backups/db_info_test.go # state/enableha.go # state/enableha_test.go # state/export_test.go # state/machine_ports.go # state/resources.go # state/secrets.go # state/secrets_test.go # state/txns.go ```
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.
When saving metadata for a secret consumer, any obsolete secret revisions are deleted.
There's a race where saving a new revision concurrently with updating a secret consumer record could result in the new secret revision being marked as obsolete.
This also could result in a mismatch between the max revision number and the latest revision attr on the parent metadata doc. This caused a "state changing too quickly" error due to an assertion used in processing the obsolete revisions.
The fixes are:
New tests
Also, a drive by fix to the firewaller unit tests.
QA steps
Not really easy to reproduce what happened on site. Unit tests were created to simulate the issue.
Links
Issue: Fixes #21778, #21783
Jira card: JUJU-9206
Jira card: JUJU-9211