fix(namespace): converge bookie affinity group instead of blind-writing - #421
Open
david-streamlio wants to merge 1 commit into
Open
Conversation
Every PulsarNamespace that did not declare `bookieAffinityGroup` issued an unconditional DELETE .../persistence/bookieAffinity on each reconcile, with no IsNotFound tolerance -- unlike the neighbouring RemoveTopicAutoCreation and RemoveInactiveTopicPolicies branches in the same function. Pulsar implements the delete as a set of a null group (NamespacesBase#internalDeleteBookieAffinityGroupAsync), and both the setter and the getter call validateSuperUserAccessAsync(). So a tenant-admin connection got 401/403 on that DELETE, ApplyNamespace failed, and the namespace never reached Ready -- for users who never touched bookie affinity at all. It also created the local-policies znode with default bundle data when there was nothing to delete. Read the current group first and write only on an actual diff. An unset-to-unset transition now touches Pulsar not at all, which removes the superuser dependency for namespaces that do not use the feature. Both of Pulsar's "no group" answers are normalized: 404 "Namespace local-policies does not exist" when the namespace has no local policies, and 200 with an empty group when they were cleared. Also: - Correct the BookieAffinityGroup godoc, which described PulsarNSIsolationPolicy -- a different feature -- and made the field read as something it is not. - Require MinLength=1 on both group names. An empty primary passed CRD validation and was pushed to Pulsar, where BookieRackAffinityMapping cannot place ledgers. - Document the end-to-end carve-out: PulsarNSIsolationPolicy for brokers paired with bookieAffinityGroup for bookies, including the rack-metadata prerequisite and the superuser requirement. The CRD schema was updated by hand rather than via `make manifests`: the Makefile pins controller-gen v0.17.0 but the committed CRDs were generated with v0.15.0, so regenerating rewrites all 19 CRDs and strips their license headers. The bookieAffinityGroup block matches the generator output exactly. The version skew is pre-existing and needs its own change. Fixes streamnative#420 Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Contributor
|
@david-streamlio:Thanks for your contribution. For this PR, do we need to update docs? |
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 #420.
spec.bookieAffinityGroupalready existed onPulsarNamespaceand was already reconciled, so this is not a new feature — it is a fix to the reconcile path, plus the validation and docs the field was missing. Rationale for the reframing is in this issue comment.The bug
Every
PulsarNamespacethat did not declarebookieAffinityGroupissued an unconditionalDELETE .../persistence/bookieAffinityon each reconcile, with noIsNotFoundtolerance — unlike the neighbouringRemoveTopicAutoCreationandRemoveInactiveTopicPoliciesbranches in the same function:Pulsar implements that delete as a set of a null group (
NamespacesBase#internalDeleteBookieAffinityGroupAsync→internalSetBookieAffinityGroupAsync(null)), and both the setter and the getter callvalidateSuperUserAccessAsync(). Consequences:ApplyNamespacefails, and the namespace never reachesReady— for users who never touched bookie affinity at all.setLocalPoliciesWithCreatecreates the local-policies znode with default bundle data even when there was nothing to delete.The fix
applyBookieAffinityGroupreads the current group first and writes only on an actual diff. An unset-to-unset transition now touches Pulsar not at all, which removes the superuser dependency for namespaces that don't use the feature. Both of Pulsar's "no group" answers are normalized to nil: a 404"Namespace local-policies does not exist"when the namespace has no local policies, and a 200 carrying an empty group when they were cleared.A non-404 read failure propagates rather than being mistaken for "unset" — otherwise a 403 would silently skip a required write.
Also in this PR
BookieAffinityGroupgodoc. It read "is the name of the namespace isolation policy to apply to the namespace", which describesPulsarNSIsolationPolicy— a different feature. That wording is why Expose bookie affinity groups (namespace-level bookie isolation) as a managed resource #420 was filed as "not exposed through any CRD".MinLength=1on both group names. An empty primary passed CRD validation and was pushed to Pulsar, whereBookieRackAffinityMappingcannot place ledgers. Only""becomes invalid, so no working configuration is rejected.docs/pulsar_namespace.mdpairingPulsarNSIsolationPolicy(brokers) withbookieAffinityGroup(bookies), including the rack-metadata prerequisite and the superuser requirement. Cross-linked fromdocs/pulsar_ns_isolation_policy.md.Tests
pkg/admin/bookie_affinity_group_test.go— 7 table cases over anhttptestserver in the style ofnamespace_backlog_quota_test.go: unset stays unset without writing, cleared group is not deleted again, removal deletes, set when absent, matching group left alone, changed group rewritten, dropped secondary rewritten. Plus read-failure propagation.tests/operator/resources_test.go— aPulsarNamespace Bookie Affinity Groupcontext covering create → update → empty-primary rejection → field removal, mirroring the offload-policies coverage from feat(namespace): add offload policies support #413.Verified:
go build ./...,go test ./pkg/... ./api/...(including the existingapplyNamespacePoliciesbacklog-quota tests, which now traverse the new read path),make fmt vet,make license-check(479 files, 0 invalid). E2E not executed — needs a live cluster viaADMIN_SERVICE_URL.Note for reviewers
The CRD schema was updated by hand, deliberately.
make manifestspulls controller-gen v0.17.0 (pinned atMakefile:201) but the committed CRDs were generated with v0.15.0. Running it rewrites all 19 CRDs — ~1300 lines of unrelated churn — and strips the Apache license header from every YAML. Only thebookieAffinityGroupblock was applied here, byte-matching the generator's output. The Makefile/CRD version skew is pre-existing and wants its own PR.The sn-operator half of #420 (declarative bookie-to-isolation-group membership on
BookKeeperCluster) is a genuinely separate gap and belongs on that repo — nothing here can express it.🤖 Generated with Claude Code