Skip to content

Commit cd2b102

Browse files
committed
feat: implement lazy auto-commit handling to prevent race conditions during store initialization
1 parent ad07c98 commit cd2b102

2 files changed

Lines changed: 34 additions & 5 deletions

File tree

nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,10 @@ private static MVStore.Builder createBuilder(MVStoreConfig mvStoreConfig) {
119119
builder = builder.fileName(mvStoreConfig.filePath());
120120
}
121121

122-
if (!mvStoreConfig.autoCommit()) {
123-
builder = builder.autoCommitDisabled();
124-
}
122+
// auto-commit is enabled after the store is fully bootstrapped (see openOrCreate),
123+
// to avoid the background writer racing with creation of the very first map/chunk
124+
// on a brand new store, which can corrupt the file (github issue: CI flakiness)
125+
builder = builder.autoCommitDisabled();
125126

126127
if (mvStoreConfig.autoCommitBufferSize() > 0) {
127128
builder = builder.autoCommitBufferSize(mvStoreConfig.autoCommitBufferSize());

nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class NitriteMVStore extends AbstractNitriteStore<MVStoreConfig> {
4545
private MVStore mvStore;
4646
private final Map<String, NitriteMap<?, ?>> nitriteMapRegistry;
4747
private final Map<String, NitriteRTree<?, ?>> nitriteRTreeMapRegistry;
48+
private volatile boolean autoCommitPending;
4849

4950
public NitriteMVStore() {
5051
super();
@@ -54,11 +55,23 @@ public NitriteMVStore() {
5455

5556
@Override
5657
public void openOrCreate() {
58+
// MVStoreUtils always opens with auto-commit disabled; it is enabled lazily
59+
// right after the very first map is created (see enableAutoCommitIfPending()),
60+
// so the background writer never races with bootstrapping a brand new store
61+
this.autoCommitPending = getStoreConfig().autoCommit();
5762
this.mvStore = MVStoreUtils.openOrCreate(getStoreConfig());
5863
initEventBus();
5964
alert(StoreEvents.Opened);
6065
}
6166

67+
private void enableAutoCommitIfPending() {
68+
if (autoCommitPending) {
69+
autoCommitPending = false;
70+
mvStore.commit();
71+
mvStore.setAutoCommitDelay(1000);
72+
}
73+
}
74+
6275
@Override
6376
public boolean isClosed() {
6477
return mvStore == null || mvStore.isClosed();
@@ -76,7 +89,20 @@ public boolean isReadOnly() {
7689

7790
@Override
7891
public void commit() {
79-
mvStore.commit();
92+
// pause the background writer for the duration of this explicit commit;
93+
// H2's autocommit thread and an explicit commit() racing on the same store
94+
// can trip an internal MVStore assertion (concurrent chunk serialization)
95+
int delay = mvStore.getAutoCommitDelay();
96+
if (delay > 0) {
97+
mvStore.setAutoCommitDelay(0);
98+
}
99+
try {
100+
mvStore.commit();
101+
} finally {
102+
if (delay > 0) {
103+
mvStore.setAutoCommitDelay(delay);
104+
}
105+
}
80106
alert(StoreEvents.Commit);
81107
}
82108

@@ -187,7 +213,9 @@ private MVMap openMVMap(String mapName, MVMap.MapBuilder builder) {
187213

188214
while (version >= 0) {
189215
try {
190-
return mvStore.openMap(mapName, mapBuilder);
216+
MVMap map = mvStore.openMap(mapName, mapBuilder);
217+
enableAutoCommitIfPending();
218+
return map;
191219
} catch (MVStoreException me) {
192220
if (version == 0) {
193221
throw me;

0 commit comments

Comments
 (0)