In-memory metastore overlay over MySQL (Phase 1, #347)#390
Conversation
…nds to HBase r/w + MySQL read-only (#347) Phase 1 overlay of metastore consolidation (#347). Extends the existing `LocalBackedJdbcHashLabel` (local H2 + MySQL) with a third HBase layer that becomes the operational read/write target, while MySQL is demoted to a read-only fallback. ## Changes - `LocalBackedJdbcHashLabel`: add `consolidatedLabel` (HBaseHashLabel) field - Write (INSERT/UPDATE): HBase only; MySQL never mutated - Write (DELETE): mirrored to MySQL so deleted rows cannot resurface on read - Read: HBase wins on (src, tgt) dedup; MySQL rows appear only when HBase has no entry for that key - Count: HBase wins on src dedup - `GraphDefaults` / `Graph`: propagate `consolidatedMetastore: Mono<HBaseTables>` - URI derived from tenant: `datastore://{tenant}/actionbase_metastore` - `.cache()` applied so the HBase Table object is not re-created per call - `GraphConfig`: add `consolidatedMetastoreUri` override (used in tests for isolation via a random namespace per Graph instance) - `GraphFixtures`: inject per-test UUID namespace to prevent mock HBase sharing - `LocalBackedJdbcHashLabelOverlayTest`: unit tests with mockk + @ObjectSource covering write routing, DELETE mirroring, read dedup, and count dedup Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
c8306ec to
15e9784
Compare
…erlay for scan support HBaseHashLabel.scan() throws UnsupportedOperationException, breaking DdlService.getAll() which issues indexName=null / prefix-only scans. Switch consolidatedLabel to HBaseIndexedLabel and inject a default prefix-scan index (no fields) so scans route through without predicates. Patch incoming indexName=null to DEFAULT_SCAN_INDEX before forwarding to HBase. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Merge mergeOverlay + mergeCountOverlay into single merge(keyOf) function - Derive indices/indexNameToIndex from indexedEntity in create() - Extract coder local variable in create() to remove duplication Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Phase 2 Migration PlanPhase 1 overlay is now live. To migrate MySQL → HBase and remove MySQL: Step 1 — Seed HBase from MySQL (insert-only) For each metadata label ( After this, every MySQL row exists in HBase. Phase 1 HBase writes are never overwritten because Step 2 — Remove MySQL from the read path Once the seed is verified (reconcile MySQL key count vs HBase key count):
Step 3 — Remove JDBC/H2 entirely (Phase 3)
|
… toggle - Mirror all writes (INSERT/UPDATE/DELETE) to MySQL for rollback safety - Add useJdbcMetastore flag (default: true); set false to bypass MySQL entirely - Refactor LocalBackedJdbcHashLabel read paths with remoteEdges/remoteCounts helpers - Wire flag through GraphConfig → GraphDefaults → LocalBackedJdbcHashLabel - Expose as kc.graph.use-jdbc-metastore in application.yaml Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The wiring step requires changing the constructor to inject labels directly (as PR #390 did) before the local label's coder type can change from String to ByteArray. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y-metastore-label # Conflicts: # engine/src/main/kotlin/com/kakao/actionbase/v2/engine/GraphDefaults.kt # engine/src/main/kotlin/com/kakao/actionbase/v2/engine/label/metastore/LocalBackedJdbcHashLabel.kt
|
Reverse-merged
|
…ing it Comment out the explicit yaml value so the code default (true) governs; a deployer sets use-jdbc-metastore: false to opt into HBase-only mode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The consolidated metastore URI hardcoded config.tenant as the HBase namespace,
which need not match the cluster's actually-configured namespace. Introduce a
NAMESPACE_SENTINEL ("__default__") that DefaultHBaseCluster.getTable resolves to
its own configured namespace, and point the default metastore URI at it. Literal
namespaces (used by tests) are unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Updated: the overlay is now an in-memory As-is (default Deploy → rollback is safe — MySQL holds every operational write, and the overlay is volatile with no persistent footprint, so rolling back to the previous version loses nothing.
Next steps (later PRs):
|
Scope Phase 1 to a deploy/rollback-safe stepping stone: the consolidated overlay is now an in-memory ByteArrayIndexedLabel instead of an HBase-backed label. Durable state stays in MySQL (mirrored on writes), so no HBase table provisioning is required to boot, and rolling back leaves no persistent artifact. Swapping the overlay to a persistent HBase-backed label is a later step. Removes the consolidatedMetastore (Mono<HBaseTables>) plumbing, the consolidatedMetastoreUri config, and the __default__ namespace sentinel that only served the HBase metastore URI. The overlay store is a dedicated ByteArrayStore, separate from the local seed store. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Optimistic merge. |
Summary
Phase 1 of metastore consolidation (#347): extend
LocalBackedJdbcHashLabelwith an operational overlay layer so the metastore is served from a fast store while MySQL becomes a read-only fallback.This PR scopes the overlay to an in-memory
ByteArrayIndexedLabel— a deploy/rollback-safe stepping stone that needs no HBase provisioning. Durable state stays in MySQL (mirrored on writes). Swapping the overlay for a persistent HBase-backed label is a later step.Part of #347
Changes
LocalBackedJdbcHashLabel— three layers: local seed (in-memory), operational overlay (in-memory), MySQL baseuseJdbcMetastore=true)(src,tgt)dedup; MySQL rows appear only when the overlay has no entryindexName=null→__default__prefix-scan index for both indexed backends; MySQL keeps the original filterJdbcHashLabelreturns a-1sentinel)useJdbcMetastore=falsebypasses MySQL entirely (overlay-only)GraphDefaults/Graph— addconsolidatedStore: ByteArrayStore(dedicated in-memory overlay store, separate from the local seed store)GraphConfig— adduseJdbcMetastoretoggle (defaulttrue)LocalBackedJdbcHashLabelOverlayTest— mockk unit tests covering write routing, DELETE mirroring, read dedup, count (overlay + local, MySQL never consulted), and lock delegationMerge policy
*MySQL merge is skipped whenuseJdbcMetastore=false.How to Test
./gradlew :engine:test— 621 tests pass.LocalBackedJdbcHashLabelOverlayTestcovers write routing, DELETE mirroring, read dedup, count (MySQL never consulted), and lock delegation;LocalBackedJdbcHashLabelTestcovers__default__scan routing and the no-sentinel count invariant.AI Assistance