Skip to content

Commit db68ac9

Browse files
Merge pull request #874 from sirixdb/refactor/rename-resource-manager-to-session
refactor: miscellaneous cleanup across core pages and storage engine
2 parents 618692a + ed06c91 commit db68ac9

File tree

8 files changed

+89
-68
lines changed

8 files changed

+89
-68
lines changed

bundles/sirix-core/src/main/java/io/sirix/access/LocalDatabase.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,9 @@ public List<Path> listResources() {
351351

352352
@Override
353353
public Transaction beginTransaction() {
354-
// FIXME
355-
return null;
354+
throw new UnsupportedOperationException(
355+
"Multi-resource transactions are not yet implemented. "
356+
+ "Use beginNodeTrx() on individual ResourceSession instances instead.");
356357
}
357358

358359
@Override

bundles/sirix-core/src/main/java/io/sirix/access/trx/node/json/JsonNodeTrxImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2365,9 +2365,9 @@ private void copy(final JsonNodeReadOnlyTrx trx, final InsertPosition insert) {
23652365
}
23662366
}
23672367
// $CASES-OMITTED$
2368-
default -> // new JsonItemShredder.Builder(this, new JsonItemIterator(new JsonItemFactory().get),
2369-
// insert).build().call();
2370-
throw new IllegalStateException(); // FIXME
2368+
default -> throw new UnsupportedOperationException(
2369+
"Copying complex JSON node kinds (OBJECT, ARRAY, OBJECT_KEY) via copySubtree* "
2370+
+ "is not yet implemented. Node kind: " + kind);
23712371
}
23722372
rtx.close();
23732373
}

bundles/sirix-core/src/main/java/io/sirix/access/trx/page/NodeStorageEngineWriter.java

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -800,32 +800,43 @@ public UberPage commit(@Nullable final String commitMessage, @Nullable final Ins
800800
setUserIfPresent();
801801
setCommitMessageAndTimestampIfRequired(commitMessage, commitTimestamp);
802802

803-
// PIPELINING: Serialize pages WHILE previous fsync may still be running
804-
// This overlaps CPU work (serialization) with IO work (fsync)
803+
// PIPELINING: Serialize pages WHILE previous fsync may still be running.
804+
// This overlaps CPU work (serialization) with IO work (fsync).
805805
parallelSerializationOfKeyValuePages();
806806

807-
// Recursively write indirectly referenced pages (serializes to buffers)
807+
// Recursively write indirectly referenced pages (serializes to buffers).
808808
uberPage.commit(this);
809809

810-
// NOW wait for previous fsync before writing to storage
811-
// This ensures ordering: previous commit is durable before new data hits disk
810+
// Wait for the previous commit's async UberPage fsync to complete.
811+
// This ensures the previous revision is fully durable before we proceed.
812812
if (pendingFsync != null) {
813813
pendingFsync.join();
814+
pendingFsync = null;
814815
}
815816

816-
// Write pages to storage (previous fsync complete, safe to write)
817+
// CRITICAL crash-safety invariant (write-ahead property):
818+
// All data pages MUST be flushed to durable storage BEFORE the UberPage is written.
819+
// If the process crashes between writing data pages and writing the UberPage, the OS
820+
// kernel may flush the UberPage before the data pages, leaving the database pointing at
821+
// pages that are not yet on disk. An explicit forceAll() here prevents that window.
822+
storagePageReaderWriter.forceAll();
823+
824+
// Write UberPage — all data pages are now durable, safe to update the root pointer.
817825
storagePageReaderWriter.writeUberPageReference(getResourceSession().getResourceConfig(), uberPageReference,
818826
uberPage, bufferBytes);
819827

820828
if (isAutoCommitting) {
821-
// Auto-commit mode: fsync runs asynchronously for better throughput
822-
// Next commit's serialization will overlap with this fsync
829+
// Auto-commit mode: queue an async fsync for the UberPage so the next commit's
830+
// serialization can overlap with this IO. The next commit will call pendingFsync.join()
831+
// before writing its own UberPage, guaranteeing ordering.
832+
// Even if the process crashes before this fsync completes the database is consistent:
833+
// the old (pre-UberPage) state is recovered because the new UberPage is not yet on disk.
823834
pendingFsync = java.util.concurrent.CompletableFuture.runAsync(() -> {
824835
storagePageReaderWriter.forceAll();
825836
});
826837
} else {
827-
// Regular commit: synchronous fsync for strict durability
828-
// Data is guaranteed durable when commit() returns
838+
// Regular commit: flush the UberPage synchronously so durability is guaranteed
839+
// before commit() returns to the caller.
829840
storagePageReaderWriter.forceAll();
830841
pendingFsync = null;
831842
}
@@ -979,6 +990,12 @@ public void close() {
979990
pendingFsync = null;
980991
}
981992

993+
// Release the demotion buffer's off-heap memory.
994+
if (demotionBuffer != null) {
995+
demotionBuffer.close();
996+
demotionBuffer = null;
997+
}
998+
982999
// Don't clear the cached containers here - they've either been:
9831000
// 1. Already cleared and returned to pool during commit(), or
9841001
// 2. Will be cleared and returned to pool by log.close() below

bundles/sirix-core/src/main/java/io/sirix/diff/algorithm/fmse/FMSE.java

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,7 @@ public void diff(final XmlNodeTrx wtx, final XmlNodeReadOnlyTrx rtx) {
191191
final var fastMatching = fastMatch(this.wtx, this.rtx);
192192
totalMatching = new Matching(fastMatching);
193193
firstFMESStep(this.wtx, this.rtx);
194-
try {
195-
secondFMESStep(this.wtx, this.rtx);
196-
} catch (final SirixException e) {
197-
logger.error(e.getMessage(), e);
198-
}
194+
secondFMESStep(this.wtx, this.rtx);
199195
}
200196

201197
/**
@@ -271,12 +267,8 @@ private void doFirstFSMEStep(final XmlNodeTrx wtx, final XmlNodeReadOnlyTrx rtx)
271267
rtx.moveTo(x);
272268
if (rtx.isNamespace() || rtx.isAttribute()) {
273269
wtx.moveTo(w);
274-
try {
275-
totalMatching.remove(w);
276-
wtx.remove();
277-
} catch (final SirixException e) {
278-
logger.error(e.getMessage(), e);
279-
}
270+
totalMatching.remove(w);
271+
wtx.remove();
280272
w = emitInsert(x, z, -1, wtx, rtx);
281273
} else {
282274
final int k = findPos(x, wtx, rtx);
@@ -446,8 +438,7 @@ private void emitMove(final long child, final long parent, final int pos, final
446438
moved = wtx.moveTo(parent);
447439
assert moved;
448440

449-
try {
450-
if (pos == 0) {
441+
if (pos == 0) {
451442
assert wtx.getKind() == NodeKind.ELEMENT || wtx.getKind() == NodeKind.XML_DOCUMENT;
452443
if (wtx.getFirstChildKey() == child) {
453444
logger.error("Something went wrong: First child and child may never be the same!");
@@ -512,9 +503,6 @@ private void emitMove(final long child, final long parent, final int pos, final
512503

513504
wtx.moveTo(wtx.moveSubtreeToRightSibling(child).getNodeKey());
514505
}
515-
} catch (final SirixException e) {
516-
logger.error(e.getMessage(), e);
517-
}
518506
}
519507

520508
private void checkFromNodeForTextRemoval(final XmlNodeTrx wtx, final long child) {
@@ -562,26 +550,22 @@ private static void emitUpdate(final long fromNode, final long toNode, final Xml
562550
wtx.moveTo(fromNode);
563551
rtx.moveTo(toNode);
564552

565-
try {
566-
switch (rtx.getKind()) {
567-
case ELEMENT, ATTRIBUTE, NAMESPACE, PROCESSING_INSTRUCTION -> {
568-
assert rtx.getKind() == NodeKind.ELEMENT || rtx.getKind() == NodeKind.ATTRIBUTE
569-
|| rtx.getKind() == NodeKind.NAMESPACE || rtx.getKind() == NodeKind.PROCESSING_INSTRUCTION;
570-
wtx.setName(rtx.getName());
571-
if (wtx.getKind() == NodeKind.ATTRIBUTE || wtx.getKind() == NodeKind.PROCESSING_INSTRUCTION) {
572-
wtx.setValue(rtx.getValue());
573-
}
574-
}
575-
case TEXT, COMMENT -> {
576-
assert wtx.getKind() == NodeKind.TEXT;
553+
switch (rtx.getKind()) {
554+
case ELEMENT, ATTRIBUTE, NAMESPACE, PROCESSING_INSTRUCTION -> {
555+
assert rtx.getKind() == NodeKind.ELEMENT || rtx.getKind() == NodeKind.ATTRIBUTE
556+
|| rtx.getKind() == NodeKind.NAMESPACE || rtx.getKind() == NodeKind.PROCESSING_INSTRUCTION;
557+
wtx.setName(rtx.getName());
558+
if (wtx.getKind() == NodeKind.ATTRIBUTE || wtx.getKind() == NodeKind.PROCESSING_INSTRUCTION) {
577559
wtx.setValue(rtx.getValue());
578560
}
579-
// $CASES-OMITTED$
580-
default -> {
581-
}
582561
}
583-
} catch (final SirixException e) {
584-
logger.error(e.getMessage(), e);
562+
case TEXT, COMMENT -> {
563+
assert wtx.getKind() == NodeKind.TEXT;
564+
wtx.setValue(rtx.getValue());
565+
}
566+
// $CASES-OMITTED$
567+
default -> {
568+
}
585569
}
586570
}
587571

@@ -611,8 +595,7 @@ private long emitInsert(final long child, final long parent, final int pos, fina
611595
wtx.moveTo(parent);
612596
rtx.moveTo(child);
613597

614-
try {
615-
switch (rtx.getKind()) {
598+
switch (rtx.getKind()) {
616599
case ATTRIBUTE -> {
617600
try {
618601
wtx.insertAttribute(rtx.getName(), rtx.getValue());
@@ -726,12 +709,8 @@ private long emitInsert(final long child, final long parent, final int pos, fina
726709
}
727710
}
728711
}
729-
} catch (final SirixException e) {
730-
logger.error(e.getMessage(), e);
731-
}
732-
733712
return wtx.getNodeKey();
734-
}
713+
}
735714

736715
/**
737716
* Remove right sibling text node from the storage as well as from the matching.

bundles/sirix-core/src/main/java/io/sirix/io/ram/RAMStorage.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,22 @@ public Writer writeUberPageReference(final ResourceConfiguration resourceConfigu
188188

189189
@Override
190190
public Instant readRevisionRootPageCommitTimestamp(int revision) {
191-
// FIXME
192-
throw new UnsupportedOperationException();
191+
final RevisionRootPage revisionRootPage = mResourceRevisionRootsStorage.get(revision);
192+
if (revisionRootPage == null) {
193+
throw new SirixIOException("No revision root page found for revision " + revision
194+
+ " in RAM storage. RAMStorage does not persist revision root pages across restarts.");
195+
}
196+
return Instant.ofEpochMilli(revisionRootPage.getRevisionTimestamp());
193197
}
194198

195199
@Override
196200
public RevisionFileData getRevisionFileData(int revision) {
197-
// FIXME
198-
throw new UnsupportedOperationException();
201+
final RevisionRootPage revisionRootPage = mResourceRevisionRootsStorage.get(revision);
202+
if (revisionRootPage == null) {
203+
throw new SirixIOException("No revision root page found for revision " + revision
204+
+ " in RAM storage. RAMStorage does not persist revision root pages across restarts.");
205+
}
206+
return new RevisionFileData(0L, Instant.ofEpochMilli(revisionRootPage.getRevisionTimestamp()));
199207
}
200208

201209
@Override

bundles/sirix-core/src/main/java/io/sirix/page/IndirectPage.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,20 @@ public IndirectPage(final Page delegate) {
5858
* Clone indirect page.
5959
*
6060
* @param page {@link IndirectPage} to clone
61+
* @throws IllegalStateException if the delegate type is unknown and cannot be copied
6162
*/
6263
public IndirectPage(final IndirectPage page) {
6364
final Page pageDelegate = page.delegate();
6465

65-
if (pageDelegate instanceof ReferencesPage4) {
66-
delegate = new ReferencesPage4((ReferencesPage4) pageDelegate);
67-
} else if (pageDelegate instanceof BitmapReferencesPage) {
68-
delegate = new BitmapReferencesPage(pageDelegate, ((BitmapReferencesPage) pageDelegate).getBitmap());
69-
} else if (pageDelegate instanceof FullReferencesPage) {
70-
delegate = new FullReferencesPage((FullReferencesPage) pageDelegate);
66+
if (pageDelegate instanceof ReferencesPage4 ref) {
67+
delegate = new ReferencesPage4(ref);
68+
} else if (pageDelegate instanceof BitmapReferencesPage bmp) {
69+
delegate = new BitmapReferencesPage(pageDelegate, bmp.getBitmap());
70+
} else if (pageDelegate instanceof FullReferencesPage full) {
71+
delegate = new FullReferencesPage(full);
72+
} else {
73+
throw new IllegalStateException(
74+
"Unknown IndirectPage delegate type, cannot clone: " + pageDelegate.getClass().getName());
7175
}
7276
}
7377

bundles/sirix-core/src/main/java/io/sirix/page/KeyValueLeafPage.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,11 @@ public long getPageKey() {
673673
}
674674

675675
@Override
676-
public DataRecord getRecord(int offset) {
676+
public DataRecord getRecord(final int offset) {
677+
if (offset < 0 || offset >= Constants.NDP_NODE_COUNT) {
678+
throw new IllegalArgumentException(
679+
"Record offset out of range [0, " + Constants.NDP_NODE_COUNT + "): " + offset);
680+
}
677681
return records[offset];
678682
}
679683

@@ -685,8 +689,13 @@ public DataRecord getRecord(int offset) {
685689
* the cached record must be cleared to force re-materialization from those bytes on the next read.
686690
*
687691
* @param offset the slot offset to clear
692+
* @throws IllegalArgumentException if offset is out of range [0, {@link Constants#NDP_NODE_COUNT})
688693
*/
689694
public void clearRecord(final int offset) {
695+
if (offset < 0 || offset >= Constants.NDP_NODE_COUNT) {
696+
throw new IllegalArgumentException(
697+
"Record offset out of range [0, " + Constants.NDP_NODE_COUNT + "): " + offset);
698+
}
690699
if (records[offset] != null) {
691700
records[offset] = null;
692701
inMemoryRecordCount--;

bundles/sirix-core/src/main/java/io/sirix/page/PageReference.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ public PageReference() {
7777
}
7878

7979
/**
80-
* Copy constructor.
80+
* Copy constructor. Creates an independent copy; the {@code pageFragments} list is deep-copied so
81+
* mutations to the copy do not affect the original.
8182
*
8283
* @param reference {@link PageReference} to copy
8384
*/
@@ -88,7 +89,9 @@ public PageReference(final PageReference reference) {
8889
databaseId = reference.databaseId;
8990
resourceId = reference.resourceId;
9091
hashInBytes = reference.hashInBytes;
91-
pageFragments = reference.pageFragments;
92+
pageFragments = reference.pageFragments != null
93+
? new ArrayList<>(reference.pageFragments)
94+
: new ArrayList<>();
9295
hash = reference.hash;
9396
}
9497

0 commit comments

Comments
 (0)