Repository navigation
Conversation
…act early when it runs low
✅ PR checks passedThe linked issue has a description and all required Shipping project fields set. Thanks! |
| private String urgentSlot(final String jobId) { | ||
| final Optional<StorageFacts> urgent = repository.get().storageFacts().filter(this::needsSpace); | ||
| final boolean reserved = | ||
| urgent.isPresent() && reserve(URGENT_COMPACTION_KEY, jobId, URGENT_MIN_GAP); | ||
| urgent.filter(facts -> reserved).ifPresent(this::logUrgency); | ||
| return reserved ? URGENT_COMPACTION_KEY : null; | ||
| } | ||
|
|
||
| private boolean needsSpace(final StorageFacts facts) { | ||
| return facts.lowOnSpace() || facts.freePercent() < config.getUrgentFreeSpacePercent(); | ||
| } |
There was a problem hiding this comment.
⚠️ Bug: Live data above 50% of the volume makes compaction run hourly forever
urgentSlot reserves an urgent compaction whenever freePercent() < urgentFreeSpacePercent (default 50). Take a healthy store whose compacted live data uses 55% of its volume: the store is steady and the guard (10%) is far from engaging, yet every urgent compaction leaves it at about 45% free. freedEnough returns false, so the slot is held only for URGENT_MIN_GAP, and then the cycle repeats. The result is a full compaction every hour, each one running withServingWritesPaused for up to maxDurationMinutes (240 by default). On large stores writes would be paused most of the time, where before this PR compaction ran once a day. The ERROR log tells you to expand the volume, but in the meantime the system behaves worse than without the feature. One fix is to back off further after a non-freeing compaction (for example, up to the regular interval) unless the guard is actually refusing writes. Another is to only treat the store as urgent when free space has dropped since the last compaction.
When a compaction cannot free space, only an engaged guard counts as urgent again before the regular interval:
private boolean needsSpace(final StorageFacts facts) {
return facts.lowOnSpace() || facts.freePercent() < config.getUrgentFreeSpacePercent();
}
// in urgentSlot: hold the slot for compactionInterval() unless facts.lowOnSpace()
final Duration gap = urgent.map(StorageFacts::lowOnSpace).orElse(false) ? URGENT_MIN_GAP : compactionInterval();
final boolean reserved = urgent.isPresent() && reserve(URGENT_COMPACTION_KEY, jobId, gap);
Was this helpful? React with 👍 / 👎
| /** | ||
| * A write the store refused before applying it, such as a storage guard refusal, is a retry-later | ||
| * signal for the caller, not a write failure: it passes through unwrapped and unlogged. | ||
| */ | ||
| private static void rethrowIfHeldBack(final Exception failure) { | ||
| final Optional<RdfStorageMaintenanceException> heldBack = | ||
| RdfStorageMaintenanceException.find(failure); | ||
| if (heldBack.isPresent()) { | ||
| throw heldBack.get(); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Quality: Bulk reconcile still logs guard refusals at ERROR
Every single-write path now calls rethrowIfHeldBack before it logs and wraps the exception. executeReconcileUpdate, which serves bulkStoreEntities, does not. While the guard refuses writes, each reconcile batch logs "Failed to bulk-store … entities in Fuseki" at ERROR with a full stack trace and wraps the RdfStorageLowDiskException. Deferral still works, because find walks the cause chain. But this goes against the stated contract that held-back writes "pass through unwrapped and unlogged", and the log fills with false errors during a low-disk period. The fix is to call rethrowIfHeldBack(e) first in that catch block.
In executeReconcileUpdate's catch block:
} catch (Exception e) {
rethrowIfHeldBack(e);
LOG.error("Failed to bulk-store {} entities in Fuseki", requests.size(), e);
throw new RuntimeException("Failed to bulk-store entities in RDF", e);
}
Was this helpful? React with 👍 / 👎
✅ Generated Sources Auto-UpdatedThe generated TypeScript types ( |
…df-storage-guard-2.1 # Conflicts: # openmetadata-service/src/main/java/org/openmetadata/service/rdf/storage/JenaFusekiStorage.java
…heck every 15 minutes
… into fix/36437-rdf-storage-guard-2.1
…df-storage-guard-2.1 # Conflicts: # openmetadata-ui/src/main/resources/ui/src/generated/api/configuration/rdfConfiguration.ts
…, and wait for the next scheduled compaction after a futile urgent one
…df-storage-guard-2.1 # Conflicts: # openmetadata-service/src/main/java/org/openmetadata/service/rdf/storage/JenaFusekiStorage.java # openmetadata-service/src/main/java/org/openmetadata/service/rdf/storage/RdfStorageMaintenanceException.java # openmetadata-service/src/test/java/org/openmetadata/service/rdf/maintenance/RdfStorageMaintenanceServiceTest.java
…df-storage-guard-2.1
…ing generation's size would refuse the compactions a full volume needs
…df-storage-guard-2.1 # Conflicts: # openmetadata-service/src/main/java/org/openmetadata/service/rdf/maintenance/RdfStorageMaintenanceService.java
…df-storage-guard-2.1
…df-storage-guard-2.1
…df-storage-guard-2.1 # Conflicts: # openmetadata-service/src/main/java/org/openmetadata/service/rdf/maintenance/RdfStorageMaintenanceService.java # openmetadata-service/src/test/java/org/openmetadata/service/rdf/maintenance/RdfStorageMaintenanceServiceTest.java
✅ Generated Sources Auto-UpdatedThe generated TypeScript types ( |
…df-storage-guard-2.1 # Conflicts: # openmetadata-service/src/main/java/org/openmetadata/service/rdf/maintenance/RdfStorageMaintenanceService.java # openmetadata-service/src/test/java/org/openmetadata/service/rdf/maintenance/RdfStorageMaintenanceServiceTest.java
…ompacts to make room and runs once more
…df-storage-guard-2.1
… into fix/36437-rdf-storage-guard-2.1
Code Review
|
| Compact |
|
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source
|
|



Describe your changes:
Fixes #36437
Backport of PR #36472 to the 2.1 release branch, on top of PR #36473, the 2.1 backport of PR #36471. The commit applies unchanged. PR #36472 explains the problem.
What this PR changes
In the Fuseki image (
docker/rdf-store; open-metadata/jena gets the same code for production):OPENMETADATA_STORAGE_MIN_FREE(10% by default), every write gets HTTP 507 Insufficient Storage and nothing is applied. Reads and compaction keep working.GET /$/openmetadata/storage(admin only) returns the volume's size, free space and reserve, and whether writes are refused.JVM_ARGS.In the server:
RDF_URGENT_COMPACTION_FREE_PERCENT(50) or the guard refusing writes, it compacts without waiting for the daily slot. Compaction copies the live data, so 50% leaves room for the copy. If compacting cannot get back above the threshold, data compaction cannot remove fills the volume: the server logs at ERROR that it needs expanding, and urgent compactions stop for one interval while the daily one still runs.The image-server contract is recorded in
ADR:2026-10-28-the-fuseki-image-refuses-writes-below-a-free-space-reserve-and-reports-its-volume.How it was tested on 2.1
docker/rdf-store.Type of change:
Checklist:
Fixes <issue-number>: <short explanation>Fixes #<issue-number>above.docs/decisions/2026-10-28-the-fuseki-image-refuses-writes-below-a-free-space-reserve-and-reports-its-volume.md.Release-stack refresh
73e9f27c315: merged the refreshed #36473, including the release branch's LDAP test fix. Spotless and the backend package build passed;StorageGuardRefusalTest,StorageFactsTest, andRdfStorageMaintenanceServiceTestpassed (26 tests, 0 failures). The maintenance/delivery/storage Java sources match the tested main guard stack. Merge #36473 first, then retarget this PR to2.1and rerun Collate compatibility.Refreshed maintenance base
Includes the recovery fixes from #36473: the shared serving pause survives a compaction budget overrun and malformed/unavailable task responses; the versioned consumer fence protects
pipelineExecutionduring rolling upgrades; health reporting continues while compaction waits. Merge conflicts preserved both the disk-reporting/507 guard and the pause-preserving compaction API.Validation at
7b956807ee6: 113 focused unit tests plus 10 recovery integration cases passed, using real MySQL and PostgreSQL with an HTTP Fuseki boundary. Coverage includes disk facts, HTTP 507 retry classification, urgent compaction, pause retention, legacy-consumer exclusion and migration handoff, and compaction-health reporting. Spotless and independent review passed; no unanswered important findings.Merge #36473 first, then retarget this PR to its release branch and rerun the Collate build. The current feature-branch base does not exist in the Collate repository. Deploy upgraded servers before publishing/rolling out the guarded Fuseki image.
The broader affected-test run executed 620 service tests and stopped at the same 5 pre-existing
RdfBatchProcessorTestfailures caused by static registry interference withRdfResourceTest. It did not reach MCP or the wider IT matrix. The focused unit suite and 10 real-database recovery ITs above passed separately. The planner'sForwardingRdfStoragereference warning concerns additive pass-through delegation; combined storage behavior is exercised by the recovery ITs.