[vpj]: Always refresh storage quota to honor mid-push quota changes - #2912
Open
eldernewborn wants to merge 5 commits into
Open
[vpj]: Always refresh storage quota to honor mid-push quota changes#2912eldernewborn wants to merge 5 commits into
eldernewborn wants to merge 5 commits into
Conversation
eldernewborn
marked this pull request as ready for review
July 15, 2026 01:57
eldernewborn
enabled auto-merge (squash)
July 15, 2026 02:21
KaiSernLim
approved these changes
Jul 15, 2026
Comment on lines
+1922
to
+1926
| refreshStorageQuota(); | ||
| // If the engine is unknown (null) assume it truncates (fail-safe); Spark reports false, MR true. | ||
| final boolean writersMayHaveTruncated = | ||
| (dataWriterComputeJob == null || dataWriterComputeJob.truncatesDataExceedingQuota()) | ||
| && new InputStorageQuotaTracker(quotaUsedByWriters).exceedQuota(totalInputDataSizeInBytes); |
Contributor
There was a problem hiding this comment.
Suggested change
| refreshStorageQuota(); | |
| // If the engine is unknown (null) assume it truncates (fail-safe); Spark reports false, MR true. | |
| final boolean writersMayHaveTruncated = | |
| (dataWriterComputeJob == null || dataWriterComputeJob.truncatesDataExceedingQuota()) | |
| && new InputStorageQuotaTracker(quotaUsedByWriters).exceedQuota(totalInputDataSizeInBytes); | |
| // If the engine is unknown (null) assume it truncates (fail-safe); Spark reports false, MR true. | |
| final boolean writersMayHaveTruncated = | |
| (dataWriterComputeJob == null || dataWriterComputeJob.truncatesDataExceedingQuota()) | |
| && inputStorageQuotaTracker.exceedQuota(totalInputDataSizeInBytes); | |
| refreshStorageQuota(); |
super nit: if the refresh is after the calculation, then a new object InputStorageQuotaTracker shouldn't need to be created right?
| c -> c.getStore(pushJobSetting.storeName)); | ||
| if (storeResponse.isError()) { | ||
| LOGGER.warn( | ||
| "Failed to refresh storage quota for store {} from controller: {}. Using cached value.", |
Contributor
There was a problem hiding this comment.
super nit: might be better to include the cached value pushJobSetting.storeStorageQuota in the log like below
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
VPJ initializes the storage quota tracker (
InputStorageQuotaTracker) from the store quota fetched near job startup. That value is then used, unchanged, to decide whether the push exceeds quota after the data writer job completes.Because the quota can change while a push is running, relying on the value cached at initialization is wrong in both directions:
QUOTA_EXCEEDEDbased on the stale, lower value.Solution
Always re-fetch the store storage quota from the controller before evaluating it, then perform a single quota check against the fresh value. This keeps the decision consistent with the current store configuration regardless of the direction of the change.
Details:
refreshStorageQuota(), invoked unconditionally at the start of the quota check.ControllerClient.retryableRequest(... getStore ...)) rather than the broader cachedgetStoreResponse(..., true), so it does not mutate other cached store settings (compression strategy, chunking, max record size) on every push.UNLIMITED_STORAGE_QUOTAto skip the check; the refresh is skipped in that case so the skip semantics are preserved.Trade-off: this adds one controller
getStorecall per push at quota-check time (skipped for unlimited/repush). This is intentional so both increases and reductions are honored.Code changes
New log lines fire at most once per push (quota-changed info line, or a warning on refresh failure), so no rate limiting is needed.
Concurrency-Specific Checks
Both reviewer and PR author to verify
synchronized,RWLock) are used where needed.ConcurrentHashMap,CopyOnWriteArrayList).The refresh and quota check run on the single job-control thread;
inputStorageQuotaTrackeris only reassigned from that flow, and no new async execution or shared concurrent state is introduced.How was this PR tested?
Added
VenicePushJobTestcases covering:QUOTA_EXCEEDEDgetStorenever called)Existing record-too-large tests still pass, and
spotlessJavaCheckpasses../gradlew :clients:venice-push-job:test --tests "com.linkedin.venice.hadoop.VenicePushJobTest.testUpdatePushJobDetails*" ./gradlew spotlessJavaCheck