From 8767ff425a0d775d0ef540aacd210ce5788792e2 Mon Sep 17 00:00:00 2001 From: rishabhdaim Date: Mon, 16 Mar 2026 14:18:00 +0530 Subject: [PATCH 1/4] OAK-12136 : add AGENTS.md for oak-store-document module --- oak-store-document/AGENTS.md | 435 +++++++++++++++++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 oak-store-document/AGENTS.md diff --git a/oak-store-document/AGENTS.md b/oak-store-document/AGENTS.md new file mode 100644 index 00000000000..856f47d1c47 --- /dev/null +++ b/oak-store-document/AGENTS.md @@ -0,0 +1,435 @@ +# AGENTS.md — oak-store-document + +## Module Overview + +Document-oriented `NodeStore` implementation persisting repository content as documents. +Source: `oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/` + +Three `DocumentStore` backends: +- **MongoDB** (`mongo/`) — production backend for clustered deployments +- **RDB** (`rdb/`) — production backend for relational databases (JDBC) +- **Memory** (`memory/`) — in-memory, **testing only** (no persistence, no clustering) + +**Documentation:** `oak-doc/src/site/markdown/nodestore/documentmk.md`, +`nodestore/document/mongo-document-store.md`, `nodestore/document/rdb-document-store.md`, +https://jackrabbit.apache.org/oak/docs/nodestore/documentmk.html + +## Key Concepts + +### Storage Model + +Two collections/tables: +- **`nodes`** — one document per JCR node, keyed by `_id` (`:`, e.g. `2:/a/b`) +- **`blobs`** — binary data when using the built-in BlobStore + +| Field | Description | +|---|---| +| `_id` | Primary key: `:` | +| `_deleted` | Map of revision → true/false (creation/deletion history) | +| `_lastRev` | Last revision written per cluster node ID | +| `_modCount` | Monotonic modification counter for optimistic locking | +| `_children` | Boolean: true if the node has child nodes | +| `_revisions` | Commit status map: revision → `c` (committed) or `b` (branch) | +| `_bc` | Branch commit marker map (Oak 1.8+) | +| `_prev` | Links to previous (split) documents | +| `_sdType` | Split document type (10=DEFAULT, 50=LEAF, 60=COMMIT_ROOT_ONLY, 70=NO_BRANCH) | + +Property updates append new revision-stamped entries rather than overwriting, preserving full history for MVCC reads. + +### Revision Model + +A `Revision` has three components — e.g. `r13f38835063-2-1`: +- **Timestamp** — 11-char hex from `System.currentTimeMillis()` +- **Counter** — disambiguates revisions created at the same millisecond +- **Cluster node ID** — identifies which cluster node created it + +A `RevisionVector` tracks the head revision across all cluster nodes simultaneously. + +### MVCC and Commit Queue + +Reads are always consistent snapshots based on a `RevisionVector`. Concurrent commits are +serialised through `CommitQueue`, which advances the head revision atomically. Optimistic +concurrency control detects conflicts via `_modCount` and revision comparisons (`Collision`). + +### Branching + +In-memory branches accumulate changes before merge. After 100,000 pending changes a branch +is automatically spilled to the backend (`_bc` field). Branch commits are identified by +revision prefix `b`. `UnmergedBranches` tracks all active in-memory branches. + +### Previous (Split) Documents + +When a document grows beyond 100 revisions or ~1 MB, aged revision history is moved to +split documents via `SplitOperations`. Split documents are linked via `_prev` and form a +hierarchical tree (type 40 = intermediate node). + +### Sweep + +`NodeDocumentSweeper` (sweep 1) fixes up uncommitted branch revisions left by a crashed +cluster node. `MissingBcSweeper2` (sweep 2) backfills the `_bc` field on documents written +before Oak 1.8. The sweep revision (`_sweepRev`) tracks how far sweep has progressed. + +## Package Layout + +| Package | Role | +|---|---| +| _(root)_ | Core model: `DocumentNodeStore`, `NodeDocument`, `Revision`, `Commit`, `CommitQueue`, `Branch`, GC classes, cluster management | +| `mongo/` | `MongoDocumentStore`, `MongoDocumentNodeStoreBuilder`, blob store, throttling, metrics | +| `rdb/` | `RDBDocumentStore`, `RDBDocumentNodeStoreBuilder`, JDBC helpers, serialization | +| `cache/` | `NodeDocumentCache`, cache invalidation, `ForwardingListener` | +| `persistentCache/` | Off-heap / disk-backed persistent cache (MapDB) for immutable caches | +| `prefetch/` | `PrefetchDispatcher` — async prefetch of child nodes | +| `secondary/` | Secondary store support for read replicas | +| `spi/` | Internal SPI interfaces (e.g. `DocumentStoreFilter`) | +| `locks/` | `DocumentNodeStoreLocks` — cluster-level distributed locking | +| `memory/` | `MemoryDocumentStore` — test-only in-memory `DocumentStore`; no persistence, no external dependencies, no cluster support | +| `init/` | Repository initialisation helpers | +| `util/` | `Utils`, `MongoConnection`, and other shared utilities | + +## Key Classes + +| Class | Role | +|---|---| +| `DocumentNodeStore` | Central `NodeStore` implementation; owns all background threads and caches | +| `DocumentStore` | Interface for the storage backend (MongoDB or RDB) | +| `NodeDocument` | Single document in the `nodes` collection; contains all revision history for a node | +| `Revision` | A single point-in-time identifier (timestamp + counter + cluster node ID) | +| `RevisionVector` | Vector clock tracking head revision across all cluster nodes | +| `Commit` / `CommitBuilder` | Represents an in-flight commit; builds `UpdateOp` instances | +| `CommitQueue` | Serialises concurrent commits; manages head revision advancement | +| `UpdateOp` | Atomic set of update operations on a single document — always use this for mutations | +| `Branch` / `UnmergedBranches` | Tracks active in-memory branch state before merge | +| `SplitOperations` | Logic for splitting large node documents into previous documents | +| `NodeDocumentSweeper` | Marks uncommitted branch revisions as rolled back after crash recovery | +| `VersionGarbageCollector` | GC entry point — orchestrates Revision GC (phase 1, MongoDB+RDB) and Full GC (phase 2, MongoDB only) | +| `JournalGarbageCollector` | Cleans up expired journal collection entries | +| `ClusterNodeInfo` | Manages cluster node ID registration and lease renewal | +| `LastRevRecoveryAgent` | Recovers `_lastRev` for crashed cluster nodes | +| `DocumentNodeStoreBuilder` | Fluent builder holding all runtime configuration | +| `DocumentNodeStoreService` | OSGi `@Component` — reads config and constructs the builder | +| `Configuration` | OSGi `@ObjectClassDefinition` — declares all configurable properties | +| `FormatVersion` | Controls read/write compatibility during rolling upgrades | + +## OSGi Configuration + +``` +Configuration.java ← OSGi @ObjectClassDefinition (all property declarations) + ↓ +DocumentNodeStoreService ← OSGi @Component; reads config, constructs builder + ↓ +DocumentNodeStoreBuilder ← Fluent builder; holds all runtime settings + ↓ (RDB variant: RDBDocumentNodeStoreBuilder) +DocumentNodeStore ← Runtime instance +``` + +OSGi PID: `org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService` + +When adding a new OSGi config property, all four files above must be updated plus tests. +Use the `/oak-store-document-add-config` skill to automate this workflow. + +## Background Operations + +| Operation | Frequency | Purpose | +|---|---|---| +| Lease renewal | Every 10s | Extends cluster node lease 2 minutes ahead | +| Background write | Periodic | Flushes accumulated `_lastRev` updates to ancestor nodes | +| Background read | Every 1s | Polls root node for external changes; invalidates mutable caches | +| Document splitting | Periodic | Splits documents exceeding 100 revisions or ~1 MB | +| Revision GC | Configurable | Removes revisions older than retention period (default 24h) | +| Journal GC | Configurable | Cleans up `journal` collection entries | + +## Caching + +| Cache | % | Contents | Invalidation | +|---|---|---|---| +| `nodeCache` | 35% | Immutable `DocumentNodeState` views | None needed (immutable) | +| `diffCache` | 30% | Diffs between revisions | None needed; also backed by persistent cache | +| `documentCache` | 16% | Mutable `NodeDocument` instances | Via background reads | +| `childrenCache` | 15% | Immutable child node lists | None needed (immutable) | +| `prevDocCache` | 4% | Immutable previous (split) documents | None needed (immutable) | + +Immutable caches (`nodeCache`, `childrenCache`, `prevDocCache`, `diffCache`) can be backed +by the **persistent cache** (MapDB) for warm restarts. + +## Garbage Collection + +GC runs in two phases via `VersionGarbageCollector`, controlled by `VersionGCOptions` / `FullGCMode`. + +**Phase 1 — Revision GC**: supports **MongoDB and RDB** +- Removes old property revisions, deleted nodes, and split documents past the retention window +- On MongoDB: runs continuously every 5 seconds (Oak 1.8+) +- On RDB: invoked manually via `RepositoryManagementMBean.startRevisionGC()` or oak-run +- CLI: `java -jar oak-run.jar revisions collect` + +**Phase 2 — Full GC**: **MongoDB only — explicitly blocked for RDB** +- Cleans orphaned property revisions within the same time window as Revision GC +- Disabled by default; must be explicitly enabled via `fullGCEnabled=true` +- Behaviour controlled by `fullGCMode` (see `FullGCMode` enum below) + +**Journal GC** (`JournalGarbageCollector`): cleans `journal` collection entries past the retention period; applies to both MongoDB and RDB. + +### Key classes + +| Class | Role | +|---|---| +| `VersionGarbageCollector` | Main GC entry point — orchestrates both Revision GC and Full GC phases | +| `VersionGCOptions` | Runtime options passed to a GC run (max age, Full GC mode, generation, etc.) | +| `VersionGCRecommendations` | Computes when GC should run and what to target based on current store state | +| `VersionGCSupport` | Base class for backend-specific document/revision queries needed by GC | +| `MongoVersionGCSupport` | `VersionGCSupport` implementation for MongoDB (`mongo/` package) | +| `RDBVersionGCSupport` | `VersionGCSupport` implementation for RDB (`rdb/` package) | +| `FullGCStatsCollector` / `FullGCStatsCollectorImpl` | Collects per-run Full GC metrics (documents scanned, revisions removed, duration) | +| `FullGCMetricsExporter` | Exports Full GC metrics to Prometheus via pushgateway | + +### FullGCMode enum + +| Value | What it cleans | +|---|---| +| `NONE` (0) | Full GC disabled | +| `GAP_ORPHANS` | Orphaned revisions in gaps only | +| `GAP_ORPHANS_EMPTYPROPS` | Gap orphans + empty properties | +| `ALL_ORPHANS` | All orphaned revisions | +| `ALL_ORPHANS_EMPTYPROPS` | All orphans + empty properties | +| `ORPHANS_EMPTYPROPS_KEEP_ONE_USER_PROPS` | Above + keep one value for user properties | +| `ORPHANS_EMPTYPROPS_KEEP_ONE_ALL_PROPS` | Above + keep one value for all properties | +| `ORPHANS_EMPTYPROPS_UNMERGED_BC` | Above + unmerged branch commits | +| `ORPHANS_EMPTYPROPS_BETWEEN_CHECKPOINTS_NO_UNMERGED_BC` | Above, scoped between checkpoints, no unmerged BC | +| `ORPHANS_EMPTYPROPS_BETWEEN_CHECKPOINTS_WITH_UNMERGED_BC` | Above, scoped between checkpoints, with unmerged BC | + +### OSGi configuration + +**Revision GC:** + +| Property | Default | Purpose | +|---|---|---| +| `versionGcMaxAgeInSecs` | `86400` (24h) | Retention window — revisions older than this are eligible for collection | + +**Full GC (MongoDB only):** + +| Property | Default | Purpose | +|---|---|---| +| `fullGCEnabled` | `false` | Enable Full GC phase | +| `fullGcMaxAgeInSecs` | `86400` (24h) | Retention window for Full GC (same semantics as `versionGcMaxAgeInSecs`) | +| `fullGCMode` | `0` (`NONE`) | Controls which orphan/property cleanup to perform (see `FullGCMode` enum) | +| `fullGCGeneration` | `0` | Generation counter to scope Full GC runs | +| `embeddedVerificationEnabled` | `true` | Verify cleaned documents are consistent before removing revisions | + +## Throttling + +**MongoDB only** — throttling is not available for RDB. Enabled via `throttlingEnabled=true`. + +`ThrottlingDocumentStoreWrapper` wraps `MongoDocumentStore` at startup and sleeps for +`throttlingTime()` ms before every write. `MongoDocumentStoreThrottlingFactorUpdater` runs +on a background scheduler and reads a throttling factor from MongoDB's `settings` collection +every `throttlingJobSchedulePeriodSecs` seconds, storing it in an `AtomicReference` +shared with the wrapper. `ThrottlingStatsCollector` records per-operation throttling metrics. + +### OSGi configuration + +| Property | Default | Purpose | +|---|---|---| +| `throttlingEnabled` | `false` | Enable throttling; system property `oak.documentstore.throttlingEnabled` | +| `throttlingTimeMillis` | — | Base delay in milliseconds | +| `throttlingJobSchedulePeriodSecs` | — | How often the factor is re-read from MongoDB | +| `disableThrottling` | `false` | Emergency kill-switch to disable throttling at runtime | + +### Key classes + +| Class | Location | Role | +|---|---|---| +| `Throttler` | root | Interface: `throttlingTime()` returns delay in ms; `NO_THROTTLING` constant returns 0 always | +| `MongoThrottlerFactory` | `mongo/` | Factory that creates `Throttler` instances — see throttler types below | +| `ThrottlingDocumentStoreWrapper` | `util/` | `DocumentStore` decorator that sleeps for `throttlingTime()` ms before every write | +| `MongoDocumentStoreThrottlingFactorUpdater` | `mongo/` | Background scheduler that reads throttling factor from MongoDB `settings` collection into an `AtomicReference` | +| `ThrottlingStatsCollector` / `ThrottlingStatsCollectorImpl` | root | Metrics collection for throttling events per operation and collection | + +### Throttler types (`MongoThrottlerFactory`) + +| Factory method | Throttler | Behaviour | +|---|---|---| +| `exponentialThrottler(threshold, oplogWindow, time)` | `ExponentialThrottler` | Scales delay by oplog window vs threshold: `> threshold` → 0ms, `≤ threshold` → 1×, `≤ threshold/2` → 2×, `≤ threshold/4` → 4×, `≤ threshold/8` → 8× the base `throttlingTime` | +| `extFactorThrottler(factor, time)` | `ExtFactorThrottler` | Delay = `time × factor` where `factor` is read from MongoDB `settings` collection by `MongoDocumentStoreThrottlingFactorUpdater`; factor ≤ 0 → no throttling | +| `noThrottler()` | `NO_THROTTLING` | Always returns 0 — used when throttling is disabled | + +## Lease Updates + +Every cluster node must continuously prove it is alive by renewing its lease in the +`clusterNodes` collection. `ClusterNodeInfo` acquires a cluster node ID on startup and +renews `leaseEnd` every **10 seconds** (pushing it 120 seconds forward). Before every +`DocumentStore` read or write, `LeaseCheckDocumentStoreWrapper` calls +`ClusterNodeInfo.performLeaseCheck()` — if `leaseEnd` has passed, the check fails per the +configured `LeaseCheckMode` and `LeaseFailureHandler` is invoked (typically stops the OSGi +bundle) to prevent a node with an expired lease from corrupting shared state. + +### LeaseCheckMode + +| Mode | Behaviour | +|---|---| +| `STRICT` | Fails immediately when `leaseEnd` is reached. Default since Oak 1.10. | +| `LENIENT` | Gives the renewal thread a short grace period to renew before failing. | +| `DISABLED` | No lease check at all. Only for testing — never use in production. | + +### Key classes + +| Class | Location | Role | +|---|---|---| +| `ClusterNodeInfo` | root | Registers cluster node ID, holds lease state, drives renewal. Constants: `DEFAULT_LEASE_DURATION_MILLIS` (120s), `DEFAULT_LEASE_UPDATE_INTERVAL_MILLIS` (10s), `DEFAULT_LEASE_FAILURE_MARGIN_MILLIS` (20s) | +| `LeaseCheckDocumentStoreWrapper` | `util/` | `DocumentStore` decorator that calls `performLeaseCheck()` before every read and write; throws `DocumentStoreException` on failure | +| `LeaseCheckMode` | root | Enum: `STRICT`, `LENIENT`, `DISABLED` | + +### OSGi configuration + +| Property | Default | Purpose | +|---|---|---| +| `leaseCheckMode` | `STRICT` | Controls how lease expiry is handled (`STRICT` / `LENIENT` / `DISABLED`) | + +System property overrides: +- `oak.documentMK.leaseDurationSeconds` — overrides lease duration (default 120s) +- `oak.documentMK.leaseCheckDisabled` — set `true` to disable lease checks (testing only) + +## Secondary Store + +An optional read-only `NodeStore` (e.g. a local Segment store) that shadows a subset of the +document store's content via `PathFilter`, acting as a path-filtered read cache. +`SecondaryStoreObserver` keeps it in sync by observing primary changes. When a node read +falls within an included path and the secondary store's revision is current, it is served from +there; otherwise falls back to the primary. Only path inclusion is supported — exclusion within +an included subtree is not possible. + +| Class | Role | +|---|---| +| `SecondaryStoreCacheService` | OSGi `@Component`; wires secondary `NodeStore`, builds cache and observer | +| `SecondaryStoreCache` | `DocumentNodeStateCache` implementation; path-filtered read delegation | +| `SecondaryStoreObserver` | Observes primary store changes and applies them to the secondary store | + +## Checkpoints + +Checkpoints pin a `RevisionVector` so GC cannot remove still-needed revisions — primarily +used by the async indexer. Created via `DocumentNodeStore.checkpoint(lifetime, info)`, released +via `DocumentNodeStore.release(checkpoint)`. All active checkpoints are stored in a single +`_id=checkpoint` document in `nodes`, keyed by revision → expiry + info map. +`Checkpoints.getOldestRevisionToKeep()` is called by GC before collecting — revisions newer +than the oldest valid checkpoint are never removed. Expired checkpoints are cleaned up lazily +every 10 `create` calls. + +## Prefetch + +`PrefetchDispatcher` pre-fetches visible external changes in a background thread before delivery +to local observers, hiding MongoDB/RDB read latency during observer dispatch. Controlled by +feature toggle `FT_PREFETCH_OAK-9780` (disabled by default); wired via +`DocumentNodeStoreBuilder.setPrefetchFeature()`. + +## Persistent Cache + +Backs immutable caches (`nodeCache`, `childrenCache`, `prevDocCache`, `diffCache`) with an +off-heap MapDB store on disk for warm restarts. `documentCache` (mutable) is never persisted. +Configured via `DocumentNodeStoreBuilder.setPersistentCache()`. Supports async write mode +(`asyncCache=true` by default) and a broadcast mechanism (`broadcast/` sub-package) for cache +invalidation across nodes. + +Key classes: `PersistentCache` (main entry point), `NodeCache`, `CacheActionDispatcher` +(async write queue), `PersistentCacheStats` (metrics). + +## DocumentDiscoveryLiteService + +OSGi `@Component` that derives and publishes a **cluster view** — a snapshot of which cluster +nodes are active, deactivating, or inactive — as a repository descriptor via `ClusterRepositoryInfo`. + +Node states (from `clusterNodes` collection): +- **active** — running with a valid up-to-date lease +- **deactivating** — lease expired but recovery not yet complete +- **inactive** — not running and all changes recovered + +The view carries a monotonically increasing **sequence number**; a `final` flag indicates +stability. If `final=false`, consumers must not act on deactivating/inactive states until the +view becomes final. Primarily consumed by Sling's discovery framework for cluster topology events. + +## Feature Toggles + +Registered in `DocumentNodeStoreService`, wired into `DocumentNodeStoreBuilder`. All follow +`FT__OAK-` naming and are **disabled by default**. + +| Toggle name | Builder method | Purpose | +|---|---|---| +| `FT_PREFETCH_OAK-9780` | `setPrefetchFeature` | Enable async pre-fetching of external changes | +| `FT_THROTTLING_OAK-9909` | `setDocStoreThrottlingFeature` | Enable write throttling on MongoDB | +| `FT_DISABLE_THROTTLING_OAK-12119` | `setDocStoreDisableThrottlingFeature` | Runtime kill-switch to disable throttling | +| `FT_NOCOCLEANUP_OAK-10660` | `setNoChildOrderCleanupFeature` | Disable child-order property cleanup | +| `FT_CANCELINVALIDATION_OAK-10595` | `setCancelInvalidationFeature` | Cancel in-flight cache invalidations | +| `FT_FULL_GC_OAK-10199` | `setDocStoreFullGCFeature` | Enable Full GC phase (MongoDB only) | +| `FT_EMBEDDED_VERIFICATION_OAK-10633` | `setDocStoreEmbeddedVerificationFeature` | Enable embedded verification during Full GC | +| `FT_AVOID_MERGE_LOCK_OAK-11720` | `setDocStoreAvoidMergeLockFeature` | Avoid acquiring merge lock where safe | +| `FT_PREV_NO_PROP_OAK-11184` | `setPrevNoPropCacheFeature` | Skip caching previous documents with no properties | + +## Cluster Support + +Each cluster node registers a `ClusterNodeInfo` document in `clusterNodes`, acquiring a unique +numeric ID (inactive IDs from crashed nodes are reused) and holding a lease renewed every 10s +(2-minute window). `LeaseCheckMode.STRICT` (default since Oak 1.10) immediately rejects writes +on lease expiry. + +**Clock skew:** Oak tolerates up to **2 seconds** of clock difference between the application +server and the database server. NTP synchronisation is required across all nodes. + +**Recovery:** When a cluster node crashes, `LastRevRecoveryAgent` recovers its `_lastRev` +updates. `NodeDocumentSweeper` then marks any uncommitted branch changes as rolled back. + +**Format version:** `FormatVersion` in the `settings` collection controls read/write +compatibility. Use `oak-run unlockUpgrade` when all cluster nodes are inactive to advance +the version. + +## Important System Properties + +| Property | Default | Purpose | +|---|---|---| +| `oak.documentMK.disableBranches` | false | Disable branch commits (not recommended for production) | +| `oak.documentMK.childrenCacheLimit` | 16 | Max child node entries per cache entry | +| `oak.mongo.maxReplicationLagMillis` | 60000 | Max MongoDB replication lag before warning | +| `oak.mongo.socketKeepAlive` | true | Enable TCP keep-alive on MongoDB connections | +| `oak.fullGC.enable` | false | Enable Full GC mode (cleans all orphaned revisions) | + +## Testing + +**JUnit 4** with Mockito 5.x. Three backends available: + +| Backend | Class | When to use | +|---|---|---| +| Memory | `MemoryDocumentStore` | Default for unit tests — no external dependencies, fast, no persistence | +| MongoDB | `MongoDocumentStore` | Required when testing cluster behaviour, replication lag, or Mongo-specific paths | +| RDB | `RDBDocumentStore` | Required when testing RDB-specific paths or JDBC behaviour | + +- `DocumentStoreFixture` — fixture abstraction supporting `MONGO`, `RDB_*`, and `MEMORY` + backends; used by `AbstractDocumentStoreTest` and related base classes +- `MongoUtils` / `AbstractMongoConnectionTest` — requires `mongod` on port 27017 (`-Dmongo.url` to override) +- `AbstractRDBConnectionTest` — configured via JDBC URL system properties +- Integration tests (IT suffix) require `-PintegrationTesting` +- The `DOCUMENT_NS` fixture in the broader test suite maps to MongoDB + +```bash +# Unit tests (uses MemoryDocumentStore — no MongoDB needed) +mvn test -pl oak-store-document -Dtest=NodeDocumentTest + +# Full test suite against MongoDB (requires mongod on localhost:27017) +mvn test -pl oak-store-document + +# Integration tests +mvn verify -pl oak-store-document -PintegrationTesting +``` + +## Common Pitfalls + +- **MongoDB required for `DOCUMENT_NS` fixture** — start `mongod` on port 27017 before + running any test that uses the `DOCUMENT_NS` fixture +- **Never bypass lease checks** — do not mock or disable `ClusterNodeInfo` lease logic in + production code paths; it prevents split-brain in multi-node clusters +- **Always use `UpdateOp` for mutations** — never write raw documents directly to + `DocumentStore`; `UpdateOp` ensures correct revision stamping and `_modCount` tracking +- **Document size limit** — MongoDB has a 16 MB hard limit per document; `SplitOperations` + must fire before this is reached. Do not accumulate unbounded revision history +- **Format version gating** — code that changes the on-disk format must be gated behind + a `FormatVersion` check to preserve rolling upgrade compatibility +- **Clock skew** — code using `Revision` timestamps must account for the 2-second tolerance; + never assume the local clock matches the backend clock +- **Adding OSGi config** — changes to `Configuration.java` require matching updates in + `DocumentNodeStoreService`, `DocumentNodeStoreBuilder`, `RDBDocumentNodeStoreBuilder`, + and all relevant test files; use the `/oak-store-document-add-config` skill From ca4db24d82c96171cb163a30b2954df7ca5802c4 Mon Sep 17 00:00:00 2001 From: rishabhdaim Date: Mon, 16 Mar 2026 17:13:49 +0530 Subject: [PATCH 2/4] OAK-12136 : exclude oak-store-document/AGENTS.md from RAT license check --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 41ab1d687d3..4b1b9f407de 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,7 @@ release.properties CONTRIBUTING.md AGENTS.md + oak-store-document/AGENTS.md CLAUDE.md .cursorrules .git/** From 7cec19bb2f98cf220238d9d9cddf65d90749f6c2 Mon Sep 17 00:00:00 2001 From: rishabhdaim Date: Mon, 16 Mar 2026 17:15:00 +0530 Subject: [PATCH 3/4] OAK-12136 : use wildcard to exclude all AGENTS.md files from RAT check --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4b1b9f407de..065bf40f934 100644 --- a/pom.xml +++ b/pom.xml @@ -92,8 +92,7 @@ release.properties CONTRIBUTING.md - AGENTS.md - oak-store-document/AGENTS.md + **/AGENTS.md CLAUDE.md .cursorrules .git/** From 2790523fad755dcd7d705fcad56d5e12090e45ee Mon Sep 17 00:00:00 2001 From: rishabhdaim Date: Mon, 16 Mar 2026 17:42:09 +0530 Subject: [PATCH 4/4] OAK-12136 : exclude all AGENTS.md files from RAT license check --- oak-parent/pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/oak-parent/pom.xml b/oak-parent/pom.xml index 453c26d42ad..a8e8c19b14b 100644 --- a/oak-parent/pom.xml +++ b/oak-parent/pom.xml @@ -257,6 +257,9 @@ apache-rat-plugin true + + **/AGENTS.md +