Skip to content

Commit 8eb8db5

Browse files
committed
fix: stage Retina primary index updates
1 parent 457d826 commit 8eb8db5

9 files changed

Lines changed: 1388 additions & 246 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2026 PixelsDB.
3+
*
4+
* This file is part of Pixels.
5+
*
6+
* Pixels is free software: you can redistribute it and/or modify
7+
* it under the terms of the Affero GNU General Public License as
8+
* published by the Free Software Foundation, either version 3 of
9+
* the License, or (at your option) any later version.
10+
*
11+
* Pixels is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* Affero GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the Affero GNU General Public
17+
* License along with Pixels. If not, see
18+
* <https://www.gnu.org/licenses/>.
19+
*/
20+
package io.pixelsdb.pixels.common.index;
21+
22+
import io.pixelsdb.pixels.index.IndexProto;
23+
24+
import java.util.Objects;
25+
26+
/**
27+
* Result of a successful primary index resolution, returned wrapped in
28+
* {@link java.util.Optional}: present = key is live; empty = key missing or
29+
* maps to an orphan / non-baseline-visible location; backend failures surface
30+
* as {@link io.pixelsdb.pixels.common.exception.IndexException}.
31+
*/
32+
public final class ResolvedPrimary
33+
{
34+
private final long rowId;
35+
private final IndexProto.RowLocation rowLocation;
36+
37+
public ResolvedPrimary(long rowId, IndexProto.RowLocation rowLocation)
38+
{
39+
this.rowId = rowId;
40+
this.rowLocation = Objects.requireNonNull(rowLocation, "rowLocation");
41+
}
42+
43+
public long getRowId()
44+
{
45+
return rowId;
46+
}
47+
48+
public IndexProto.RowLocation getRowLocation()
49+
{
50+
return rowLocation;
51+
}
52+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2026 PixelsDB.
3+
*
4+
* This file is part of Pixels.
5+
*
6+
* Pixels is free software: you can redistribute it and/or modify
7+
* it under the terms of the Affero GNU General Public License as
8+
* published by the Free Software Foundation, either version 3 of
9+
* the License, or (at your option) any later version.
10+
*
11+
* Pixels is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* Affero GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the Affero GNU General Public
17+
* License along with Pixels. If not, see
18+
* <https://www.gnu.org/licenses/>.
19+
*/
20+
package io.pixelsdb.pixels.common.index;
21+
22+
import io.pixelsdb.pixels.index.IndexProto;
23+
24+
import java.util.Objects;
25+
26+
/**
27+
* Journal record for restoring one primary index pointer from newRowId
28+
* back to oldRowId. restorePrimaryIndexEntries writes back oldRowId only when
29+
* the current pointer still equals newRowId, skipping entries that have
30+
* been tombstoned or moved on to a third rowId.
31+
*/
32+
public final class RollbackEntry
33+
{
34+
private final IndexProto.IndexKey indexKey;
35+
private final long oldRowId;
36+
private final long newRowId;
37+
38+
public RollbackEntry(IndexProto.IndexKey indexKey, long oldRowId, long newRowId)
39+
{
40+
this.indexKey = Objects.requireNonNull(indexKey, "indexKey");
41+
this.oldRowId = oldRowId;
42+
this.newRowId = newRowId;
43+
}
44+
45+
public IndexProto.IndexKey getIndexKey()
46+
{
47+
return indexKey;
48+
}
49+
50+
public long getOldRowId()
51+
{
52+
return oldRowId;
53+
}
54+
55+
public long getNewRowId()
56+
{
57+
return newRowId;
58+
}
59+
}

pixels-common/src/main/java/io/pixelsdb/pixels/common/index/service/IndexService.java

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@
2121

2222
import io.pixelsdb.pixels.common.exception.IndexException;
2323
import io.pixelsdb.pixels.common.index.IndexOption;
24+
import io.pixelsdb.pixels.common.index.ResolvedPrimary;
25+
import io.pixelsdb.pixels.common.index.RollbackEntry;
2426
import io.pixelsdb.pixels.index.IndexProto;
27+
2528
import java.util.List;
29+
import java.util.Optional;
2630

2731
public interface IndexService
2832
{
@@ -40,7 +44,7 @@ public interface IndexService
4044
/**
4145
* Lookup a unique index.
4246
* @param key the index key
43-
* @return the row location or null if the index entry is not found
47+
* @return the row location, or null if the key is missing or maps to an orphan
4448
*/
4549
IndexProto.RowLocation lookupUniqueIndex(IndexProto.IndexKey key, IndexOption indexOption) throws IndexException;
4650

@@ -87,6 +91,7 @@ boolean putSecondaryIndexEntries(long tableId, long indexId,
8791

8892
/**
8993
* Delete an entry from the primary index. The deleted index entry is marked as deleted using a tombstone.
94+
* Crash-unsafe; prefer {@link #resolvePrimary} + {@link #deletePrimaryIndexEntriesOnly}.
9095
* @param key the index key
9196
* @return the row location of the deleted index entry
9297
* @throws IndexException if no existing entry to delete
@@ -103,6 +108,7 @@ boolean putSecondaryIndexEntries(long tableId, long indexId,
103108

104109
/**
105110
* Delete entries from the primary index. Each deleted index entry is marked as deleted using a tombstone.
111+
* Crash-unsafe; prefer {@link #resolvePrimary} + {@link #deletePrimaryIndexEntriesOnly}.
106112
* @param tableId the table id of the index
107113
* @param indexId the index id of the index
108114
* @param keys the keys of the entries to delete
@@ -126,6 +132,7 @@ List<Long> deleteSecondaryIndexEntries(long tableId, long indexId,
126132

127133
/**
128134
* Update the entry of a primary index.
135+
* Crash-unsafe; prefer DELETE + INSERT.
129136
* @param indexEntry the index entry to update
130137
* @return the previous row location of the index entry
131138
* @throws IndexException if no existing entry to update
@@ -142,6 +149,7 @@ List<Long> deleteSecondaryIndexEntries(long tableId, long indexId,
142149

143150
/**
144151
* Update the entries of a primary index.
152+
* Crash-unsafe; prefer DELETE + INSERT.
145153
* @param tableId the table id of the primary index
146154
* @param indexId the index id of the primary index
147155
* @param indexEntries the index entries to update
@@ -215,5 +223,112 @@ boolean flushIndexEntriesOfFile(long tableId, long indexId,
215223
* @return true on success
216224
*/
217225
boolean removeIndex(long tableId, long indexId, boolean isPrimary, IndexOption option) throws IndexException;
226+
227+
// ==================================================================================
228+
// Staged primary-index APIs. Default implementations throw UnsupportedOperationException;
229+
// LocalIndexService provides the in-process implementation.
230+
// ==================================================================================
231+
232+
/**
233+
* Resolve a batch of primary index keys to {@link ResolvedPrimary} (rowId + RowLocation),
234+
* positionally aligned with keys. Returns Optional.empty() for keys
235+
* that are missing, tombstoned, orphan in MainIndex, or filtered out by the
236+
* baseline visible file set; throws on backend error.
237+
*
238+
* @param tableId the table id of the primary index
239+
* @param indexId the index id of the primary index
240+
* @param keys the primary index keys to resolve
241+
* @param indexOption optional index option
242+
* @return positional list of resolved primaries
243+
* @throws IndexException on backend error
244+
*/
245+
default List<Optional<ResolvedPrimary>> resolvePrimary(long tableId, long indexId,
246+
List<IndexProto.IndexKey> keys, IndexOption indexOption) throws IndexException
247+
{
248+
throw new UnsupportedOperationException(
249+
"resolvePrimary is not supported by this IndexService scheme");
250+
}
251+
252+
/**
253+
* Write rowId -> RowLocation entries into the main index.
254+
*
255+
* @param tableId the table id of the main index
256+
* @param entries the entries to persist
257+
* @throws IndexException on backend error
258+
*/
259+
default void putMainIndexEntriesOnly(long tableId,
260+
List<IndexProto.PrimaryIndexEntry> entries) throws IndexException
261+
{
262+
throw new UnsupportedOperationException(
263+
"putMainIndexEntriesOnly is not supported by this IndexService scheme");
264+
}
265+
266+
/**
267+
* Write IndexKey -> rowId entries into the primary single point index.
268+
*
269+
* @param tableId the table id of the primary index
270+
* @param indexId the index id of the primary index
271+
* @param entries the entries to persist
272+
* @param indexOption optional index option
273+
* @throws IndexException on backend error
274+
*/
275+
default void putPrimaryIndexEntriesOnly(long tableId, long indexId,
276+
List<IndexProto.PrimaryIndexEntry> entries, IndexOption indexOption) throws IndexException
277+
{
278+
throw new UnsupportedOperationException(
279+
"putPrimaryIndexEntriesOnly is not supported by this IndexService scheme");
280+
}
281+
282+
/**
283+
* Delete primary index entries for keys already resolved by {@link #resolvePrimary}.
284+
* Repeating on an already-deleted key is a no-op.
285+
*
286+
* @param tableId the table id of the primary index
287+
* @param indexId the index id of the primary index
288+
* @param resolvedKeys the keys to delete
289+
* @param indexOption optional index option
290+
* @throws IndexException on backend error
291+
*/
292+
default void deletePrimaryIndexEntriesOnly(long tableId, long indexId,
293+
List<IndexProto.IndexKey> resolvedKeys, IndexOption indexOption) throws IndexException
294+
{
295+
throw new UnsupportedOperationException(
296+
"deletePrimaryIndexEntriesOnly is not supported by this IndexService scheme");
297+
}
298+
299+
/**
300+
* Update primary index entries to the new IndexKey -> rowId mapping;
301+
* does not look up the previous rowId.
302+
*
303+
* @param tableId the table id of the primary index
304+
* @param indexId the index id of the primary index
305+
* @param entries the new IndexKey -> rowId mappings
306+
* @param indexOption optional index option
307+
* @throws IndexException on backend error
308+
*/
309+
default void updatePrimaryIndexEntriesOnly(long tableId, long indexId,
310+
List<IndexProto.PrimaryIndexEntry> entries, IndexOption indexOption) throws IndexException
311+
{
312+
throw new UnsupportedOperationException(
313+
"updatePrimaryIndexEntriesOnly is not supported by this IndexService scheme");
314+
}
315+
316+
/**
317+
* Restore primary index entries to oldRowId where the current pointer
318+
* still equals newRowId; skip otherwise. Intended for single-threaded
319+
* rollback windows and does not require atomic conditional update from the backend.
320+
*
321+
* @param tableId the table id of the primary index
322+
* @param indexId the index id of the primary index
323+
* @param entries rollback entries describing each oldRowId -> newRowId transition
324+
* @param indexOption optional index option
325+
* @throws IndexException on backend error
326+
*/
327+
default void restorePrimaryIndexEntries(long tableId, long indexId,
328+
List<RollbackEntry> entries, IndexOption indexOption) throws IndexException
329+
{
330+
throw new UnsupportedOperationException(
331+
"restorePrimaryIndexEntries is not supported by this IndexService scheme");
332+
}
218333
}
219334

0 commit comments

Comments
 (0)