[controller][admin-tool][protocol] UpdateStore path for storageMode and externalStorageReadMode#2823
Open
sixpluszero wants to merge 2 commits into
Open
[controller][admin-tool][protocol] UpdateStore path for storageMode and externalStorageReadMode#2823sixpluszero wants to merge 2 commits into
sixpluszero wants to merge 2 commits into
Conversation
externalStorageReadMode Activates StoreMetaValue v44 / AdminOperation v99 (ported from linkedin#2817), then wires the two new fields end-to-end through the controller UpdateStore path and the admin-tool CLI so operators can mutate them at runtime. - build.gradle: remove the v43/v98 Avro versionOverrides so the compiler picks v44/v99 by the default max-numeric rule. - AvroProtocolDefinition: METADATA_SYSTEM_SCHEMA_STORE 43->44 and ADMIN_OPERATION 98->99 so the in-process protocol version matches the compiled schema. - New enums: StorageMode {INTERNAL, DUAL_WRITE, EXTERNAL} (per-version, mirrors StoreVersion.storageMode) and ExternalStorageReadMode {VENICE_ONLY, DUAL_MODE_CONSISTENCY_CHECK, DUAL_MODE_EARLY_RETURN, EXTERNAL_ONLY} (per-store, mirrors StoreProperties.externalStorageReadMode). - Java accessors backed directly by the Avro records on ZKStore / VersionImpl; ReadOnly* / SystemStore / StoreInfo wired accordingly, cloneVersion propagates storageMode. - Schema doc strings in v44 StoreMetaValue.avsc and v99 AdminOperation.avsc switched from U+2014 em dashes to ASCII '--' for compatibility with tooling that does not handle non-ASCII. - ControllerApiConstants: new STORAGE_MODE and EXTERNAL_STORAGE_READ_MODE keys. - UpdateStoreQueryParams: setStorageMode/getStorageMode and setExternalStorageReadMode/getExternalStorageReadMode round-trip via the existing name/valueOf pattern. - VeniceParentHelixAdmin.updateStore: serializes both fields onto UpdateStore admin op via addToUpdatedConfigList; storageMode falls back to the schema default (INTERNAL) when not set (no Store-level accessor exists); externalStorageReadMode falls back to currStore. - VeniceHelixAdmin.internalUpdateStore: externalStorageReadMode applies to the Store; storageMode (per-version) is broadcast onto every existing Version via storeMetadataUpdate. The existing top-of-method regionsFilter early-return gates both fields, so --regions-filter just works. - AdminExecutionTask: extracts message.storageMode and message.externalStorageReadMode and feeds them back into the reconstructed UpdateStoreQueryParams via the existing fromInt-style pattern. - Arg.STORAGE_MODE (--storage-mode / -smd) and Arg.EXTERNAL_STORAGE_READ_MODE (--external-storage-read-mode / -esrm). - AdminTool.getUpdateStoreQueryParams wires both via genericParam + Enum::valueOf. - Command.UPDATE_STORE registers both new flags so --help lists them. - New code behind a config: no. - New log lines: no. - New unit tests: StorageModeTest, ExternalStorageReadModeTest (ported); UpdateStoreQueryParamsTest gains round-trip + empty-state tests for both new fields; TestVeniceParentHelixAdmin.testUpdateStoreStorageMode\ AndExternalStorageReadMode verifies the admin-op message carries both field values, both names land in updatedConfigsList, regionsFilter travels but stays out of updatedConfigsList; TestAdminTool.testAdminUpdateStoreArgStorageModeAndExternalStorageRead\ Mode exercises the CLI + --regions-filter together. - New integration tests: TestUpdateStoreExternalStorage covers (a) propagation to all child regions when no regions filter is set, with storageMode applied to every existing version and externalStorageRead\ Mode applied at the Store level; (b) regionsFilter restricting the update to a single child region so excluded regions stay at defaults. - Modified or extended existing tests: yes (TestStoreInfo / TestZKStore / TestVersion / TestVeniceParentHelixAdmin / TestAdminTool / UpdateStoreQueryParamsTest). - Verified backward compatibility: yes (v44/v99 only add fields with defaults; older readers ignore unknown fields). Activating the v44 / v99 schemas changes what the controller writes to ZK and what controllers exchange via the admin topic. All Venice services must be redeployed at this version before any caller starts setting storageMode / externalStorageReadMode to non-default values, otherwise a v43-only service performing a read-modify-write could silently drop the new fields. - No (defaults preserve existing behavior; new fields are inert until callers opt in). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR activates newly staged Avro protocol/schema versions (StoreMetaValue v44, AdminOperation v99) and wires two external-storage-related fields end-to-end so operators can set them via UpdateStore (including --regions-filter) and the admin-tool CLI.
Changes:
- Enable StoreMetaValue v44 / AdminOperation v99 and add Java enum/accessor plumbing for
Version.storageModeandStore.externalStorageReadMode. - Extend controller
UpdateStoreserialization/consumption paths to carry/applystorageModeandexternalStorageReadMode(with regions-filter scoping). - Add admin-tool CLI flags for both fields and add unit/integration coverage, plus ASCII-only docstring cleanup in the Avro schema files.
Reviewed changes
Copilot reviewed 29 out of 30 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| services/venice-controller/src/test/java/com/linkedin/venice/controller/TestVeniceParentHelixAdmin.java | Adds unit coverage asserting the admin-op message carries both new fields and tracks them in updatedConfigsList. |
| services/venice-controller/src/main/resources/avro/AdminOperation/v99/AdminOperation.avsc | Updates schema docs (ASCII --) and includes the new UpdateStore fields. |
| services/venice-controller/src/main/java/com/linkedin/venice/controller/VeniceParentHelixAdmin.java | Serializes storageMode / externalStorageReadMode onto the UpdateStore admin op and tracks updated-config keys. |
| services/venice-controller/src/main/java/com/linkedin/venice/controller/VeniceHelixAdmin.java | Applies storageMode (per-version) and externalStorageReadMode (per-store) in the child controller update path. |
| services/venice-controller/src/main/java/com/linkedin/venice/controller/kafka/consumer/AdminExecutionTask.java | Reconstructs UpdateStoreQueryParams from the admin-op message, including the two new fields. |
| internal/venice-test-common/src/integrationTest/java/com/linkedin/venice/endToEnd/TestUpdateStoreExternalStorage.java | Adds multi-region integration coverage for propagation + regionsFilter scoping of the new fields. |
| internal/venice-common/src/test/java/com/linkedin/venice/meta/TestZKStore.java | Tests default/round-trip/clone/Avro-model persistence for externalStorageReadMode. |
| internal/venice-common/src/test/java/com/linkedin/venice/meta/TestVersion.java | Tests default/round-trip/clone/Avro-model persistence for storageMode. |
| internal/venice-common/src/test/java/com/linkedin/venice/meta/TestStoreInfo.java | Tests StoreInfo defaulting and round-trip behavior for externalStorageReadMode. |
| internal/venice-common/src/test/java/com/linkedin/venice/meta/StorageModeTest.java | Adds enum int-mapping coverage for StorageMode. |
| internal/venice-common/src/test/java/com/linkedin/venice/meta/ExternalStorageReadModeTest.java | Adds enum int-mapping coverage for ExternalStorageReadMode. |
| internal/venice-common/src/test/java/com/linkedin/venice/controllerapi/UpdateStoreQueryParamsTest.java | Adds query-param round-trip + unset-state tests for both new fields. |
| internal/venice-common/src/main/resources/avro/StoreMetaValue/v44/StoreMetaValue.avsc | ASCII-only docstring cleanup for the staged v44 fields. |
| internal/venice-common/src/main/java/com/linkedin/venice/serialization/avro/AvroProtocolDefinition.java | Bumps active protocol versions for AdminOperation and StoreMetaValue. |
| internal/venice-common/src/main/java/com/linkedin/venice/meta/ZKStore.java | Implements Store#get/setExternalStorageReadMode() backed by the Avro data model. |
| internal/venice-common/src/main/java/com/linkedin/venice/meta/VersionImpl.java | Implements Version#get/setStorageMode() and propagates it via cloneVersion(). |
| internal/venice-common/src/main/java/com/linkedin/venice/meta/Version.java | Adds the new storageMode getter/setter to the Version interface. |
| internal/venice-common/src/main/java/com/linkedin/venice/meta/SystemStore.java | Delegates getExternalStorageReadMode() and blocks mutation. |
| internal/venice-common/src/main/java/com/linkedin/venice/meta/StoreInfo.java | Adds externalStorageReadMode to the JSON-facing StoreInfo model and mirrors it from Store. |
| internal/venice-common/src/main/java/com/linkedin/venice/meta/Store.java | Adds store-level externalStorageReadMode getter/setter to the Store interface. |
| internal/venice-common/src/main/java/com/linkedin/venice/meta/StorageMode.java | Introduces the StorageMode enum with int mapping. |
| internal/venice-common/src/main/java/com/linkedin/venice/meta/ReadOnlyStore.java | Delegates new getters and throws on new setters in read-only wrappers. |
| internal/venice-common/src/main/java/com/linkedin/venice/meta/ExternalStorageReadMode.java | Introduces the ExternalStorageReadMode enum with int mapping. |
| internal/venice-common/src/main/java/com/linkedin/venice/controllerapi/UpdateStoreQueryParams.java | Adds query-param plumbing for storageMode and externalStorageReadMode. |
| internal/venice-common/src/main/java/com/linkedin/venice/controllerapi/ControllerApiConstants.java | Adds API parameter keys for the two new fields. |
| clients/venice-admin-tool/src/test/java/com/linkedin/venice/TestAdminTool.java | Adds CLI parse-path tests for the two new flags (including --regions-filter). |
| clients/venice-admin-tool/src/main/java/com/linkedin/venice/Command.java | Registers the two new flags under UPDATE_STORE. |
| clients/venice-admin-tool/src/main/java/com/linkedin/venice/Arg.java | Defines --storage-mode and --external-storage-read-mode arguments. |
| clients/venice-admin-tool/src/main/java/com/linkedin/venice/AdminTool.java | Wires the new CLI args into UpdateStoreQueryParams. |
| build.gradle | Unpins Avro compilation overrides so the latest schema versions (v44/v99) are generated. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+3569
to
+3574
| // storageMode has no Store-level Java accessor (it lives on StoreVersion); seed from the | ||
| // schema default (INTERNAL) so an update that omits storageMode does not silently downgrade | ||
| // the existing per-version value when the child applies the admin op. | ||
| setStore.storageMode = storageMode.map(addToUpdatedConfigList(updatedConfigsList, STORAGE_MODE)) | ||
| .map(StorageMode::getValue) | ||
| .orElse(StorageMode.INTERNAL.getValue()); |
Comment on lines
820
to
823
| { | ||
| "name": "storageMode", | ||
| "doc": "Store-level default storage mode. The controller copies this value into StoreVersion.storageMode when a new store version is created; existing versions are unaffected. Controls where version data is persisted in addition to (or instead of) Venice local storage. 0 => INTERNAL (default, Venice-only); 1 => DUAL_WRITE (data is written to both Venice local storage and the configured external storage; the specific dual-write implementation — leader-consumer-pipeline vs Venice Push Job — is selected by separate server/VPJ configuration); 2 => EXTERNAL (external-storage-only; Venice's data partition becomes NoOp while metadata partitions are still persisted locally for checkpointing).", | ||
| "doc": "Store-level default storage mode. The controller copies this value into StoreVersion.storageMode when a new store version is created; existing versions are unaffected. Controls where version data is persisted in addition to (or instead of) Venice local storage. 0 => INTERNAL (default, Venice-only); 1 => DUAL_WRITE (data is written to both Venice local storage and the configured external storage; the specific dual-write implementation -- leader-consumer-pipeline vs Venice Push Job -- is selected by separate server/VPJ configuration); 2 => EXTERNAL (external-storage-only; Venice's data partition becomes NoOp while metadata partitions are still persisted locally for checkpointing).", | ||
| "type": "int", |
… copy to new versions at creation The previous implementation in this PR applied UpdateStore.storageMode by iterating store.getVersions() and calling setStorageMode on every Version, which (a) violates the schema doc that says "existing versions are unaffected" and (b) throws UnsupportedOperationException at runtime because ZKStore.getVersions() returns ReadOnlyVersion wrappers. Switch to the schema-doc semantics: UpdateStore.storageMode is a store-level default; existing StoreVersion records stay untouched; new versions inherit the current default at creation time. - v44 StoreMetaValue StoreProperties: add storageMode (int, default 0) alongside externalStorageReadMode. v44 is not yet in production (still staged by PR linkedin#2817's versionOverrides removal), so amending it in place is safer than bumping to v45. - Store interface: add getStorageMode / setStorageMode at the store level (distinct from the per-version Version.getStorageMode/setStorageMode added by PR linkedin#2817). ZKStore wires both to the new storeProperties.storageMode Avro field; ReadOnlyStore delegates the getter and throws UOE on the setter; SystemStore delegates to zkSharedStore and throws on the setter; StoreInfo.fromStore mirrors the field for JSON. ZKStore copy constructor propagates store-level storageMode like every other store-level field. - VeniceHelixAdmin.internalUpdateStore: storageMode.ifPresent now applies store.setStorageMode(mode) only -- no version iteration -- so the ReadOnlyVersion UOE from the prior commit is gone. - AbstractStore.addVersion (the !isClonedVersion branch that seeds compressionStrategy/chunkingEnabled/blobDbEnabled/... on a new version) also seeds version.setStorageMode(getStorageMode()). New pushes pick up the current store-level default; existing versions are never retroactively rewritten. - TestZKStore: store-level storageMode default / round-trip / clone preservation / "persists through Avro data model" tests, plus an addVersion test that confirms (a) a freshly added version inherits the store-level default at creation time and (b) a later store-level change does NOT mutate the previously-added version. - TestUpdateStoreExternalStorage (integration): reworked to match the store-level semantics: assert UpdateStore propagates the store-level default to all child regions, v1 (created before the update) stays at INTERNAL, a subsequent empty-push creates v2 which inherits the new default. The regions-filter test now asserts dc0 receives the store-level update while dc1 stays at the schema defaults. - New code behind a config: no. - New log lines: no. - No race conditions: storageMode is a plain int on the existing storeProperties record, accessed under the same locks as every other store-level field. - v44 schema gains a new field with default 0, so v43 readers still parse v44 records (unknown field ignored). All Venice services must be redeployed at this version before any caller starts setting non-default storageMode, since a v43-only service performing a read-modify-write could silently drop the new field. - No (defaults preserve existing behavior). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Problem Statement
Predecessor PRs in this stack staged
StoreMetaValuev44 andAdminOperationv99 with the following external-storage-related fields:StoreVersion(per-version):storageMode.StoreProperties(per-store):externalStorageReadMode.UpdateStore(admin op):storageMode,externalStorageReadMode(operator-override surface).Two follow-ups are still needed before operators can actually use these fields:
build.gradle'sversionOverrideslist pins the Avro compiler to v43 / v98, so v44 / v99 are not yet wire-real. Downstream consumers need typed Java accessors and a live admin-op surface to integrate against. ([venice-common][controller][compact] Activate StoreMetaValue v44 / AdminOperation v99 + Java accessors #2817 covers this for Java accessors but has not been ported into this fork.)UpdateStorepath that mutates the new fields, and no admin-tool CLI option to drive them. Operators need a way to set them with the usual--regions-filterscoping.Separately, the schema doc strings staged in v44 / v99 use U+2014 em dashes, which trips tooling and review pipelines that assume ASCII.
Solution
This PR bundles three changes:
1. Activate v44 / v99 + Java accessors (ported from #2817). Drop the v43/v98 entries from
build.gradle'sversionOverridesso the Avro generator picks the latest schema directories by the default max-numeric rule. BumpAvroProtocolDefinition.METADATA_SYSTEM_SCHEMA_STORE43 -> 44 andAvroProtocolDefinition.ADMIN_OPERATION98 -> 99 in lockstep. Add:StorageMode { INTERNAL, DUAL_WRITE, EXTERNAL }-- mirrorsStoreVersion.storageMode.ExternalStorageReadMode { VENICE_ONLY, DUAL_MODE_CONSISTENCY_CHECK, DUAL_MODE_EARLY_RETURN, EXTERNAL_ONLY }-- mirrorsStoreProperties.externalStorageReadMode.Store#get/setExternalStorageReadModebacked by the AvroStorePropertiesrecord onZKStore;Version#get/setStorageModebacked by the AvroStoreVersionrecord onVersionImpl.ReadOnlyStore/SystemStoredelegate getters and throw on setters.StoreInfo.fromStoremirrorsexternalStorageReadModefor JSON.cloneVersionpropagatesstorageMode.2. Controller
UpdateStorepath for the two new fields (with regions-filter support). Following the existingingestionPauseModepattern end-to-end:ControllerApiConstants: new keysSTORAGE_MODE,EXTERNAL_STORAGE_READ_MODE.UpdateStoreQueryParams:setStorageMode/getStorageModeandsetExternalStorageReadMode/getExternalStorageReadModeround-trip via the existing.name()/valueOfpattern.VeniceParentHelixAdmin.updateStore: serializes both fields onto theUpdateStoreadmin op viaaddToUpdatedConfigList.storageModefalls back to the schema default (INTERNAL) when not provided (there is no Store-level Java accessor for it);externalStorageReadModefalls back tocurrStore.getExternalStorageReadMode().VeniceHelixAdmin.internalUpdateStore:externalStorageReadModeapplies to the store;storageMode(per-version) is broadcast onto every existingVersionvia the samestoreMetadataUpdatelambda. The existing top-of-methodregionsFilterearly-return gates both new fields, so--regions-filterjust works.AdminExecutionTask: extractsmessage.storageMode/message.externalStorageReadModeand feeds them back into the reconstructedUpdateStoreQueryParamsviaStorageMode.valueOf(int)/ExternalStorageReadMode.valueOf(int).3. Admin-tool CLI for the two new fields.
Arg.STORAGE_MODE(--storage-mode/-smd) andArg.EXTERNAL_STORAGE_READ_MODE(--external-storage-read-mode/-esrm).AdminTool.getUpdateStoreQueryParamswires both viagenericParam+Enum::valueOf.Command.UPDATE_STOREregisters both new flags so--helplists them.4. Em-dash cleanup in the schema files. Two U+2014 em dashes in
StoreMetaValue/v44/StoreMetaValue.avscandAdminOperation/v99/AdminOperation.avscdoc strings are replaced with ASCII--.Code changes
Concurrency-Specific Checks
storeProperties/storeVersioncontainers and the existingUpdateStoreadmin op, accessed under the same synchronization invariants as the fields they sit alongside.Stringfield per type.How was this PR tested?
StorageModeTest,ExternalStorageReadModeTest(VeniceEnumValueTest-based int-mapping coverage; ported from [venice-common][controller][compact] Activate StoreMetaValue v44 / AdminOperation v99 + Java accessors #2817).UpdateStoreQueryParamsTest: round-trip + empty-state tests for both new fields.TestVeniceParentHelixAdmin.testUpdateStoreStorageModeAndExternalStorageReadMode: verifies the admin-op message carries both field values, both names land inupdatedConfigsList, andregionsFiltertravels on the message but stays out ofupdatedConfigsList.TestAdminTool.testAdminUpdateStoreArgStorageModeAndExternalStorageReadMode: exercises the CLI parse path together with--regions-filter.TestUpdateStoreExternalStorage(multi-region) covers (a) propagation to all child regions when no regions filter is set --storageModeapplied to every existing version,externalStorageReadModeapplied at the store level; (b)regionsFilterrestricting the update to a single child region so excluded regions stay at defaults.TestStoreInfo,TestZKStore,TestVersion(defaults / round-trip / clone preservation / "persists through Avro data model"),TestVeniceParentHelixAdmin,TestAdminTool,UpdateStoreQueryParamsTest.Rollout note
Activating the v44 / v99 schemas changes what the controller writes to ZK and what controllers exchange via the admin topic. All Venice services must be redeployed at this version before any caller starts setting
storageMode/externalStorageReadModeto non-default values; otherwise a v43-only service performing a read-modify-write could silently drop the new fields.Does this PR introduce any user-facing or breaking changes?
StorageMode.INTERNAL,ExternalStorageReadMode.VENICE_ONLY); the admin-tool flags and admin-op fields are inert until callers explicitly set them.