You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Noticed while working on #6129 (PR #6140), where the placeholder turned out to be the least valuable of the three shapes that issue listed - and the one behind the still-open #6141.
Placeholders are still reachable, but they are the pre-chunk mechanism and there is exactly one way to get one
LocalBucket has a single site that creates a placeholder pointer, in the "record must spill out of the page" branch of updateRecordInternal:
intavailableSpaceInCurrentPage = (int) (recordSize[0] + recordSize[1]);
if (lastRecordPositionInPage == recordPositionInPage)
availableSpaceInCurrentPage += page.getMaxContentSize() - pageOccupiedInBytes;
// TODO: LOOK FOR 1/2 OF THE RECORD SIZEif (availableSpaceInCurrentPage < 2 + LONG_SERIALIZED_SIZE + INT_SERIALIZED_SIZE) {
page.writeNumber(recordPositionInPage, RECORD_PLACEHOLDER_POINTER);
finalRIDrealRID = createRecordInternal(record, true, false); // the only isPlaceHolder=true call in the class
...
} else {
// SPLIT THE RECORD IN CHUNKS ... ISSUE https://github.com/ArcadeData/arcadedb/issues/332
So a placeholder happens when, and only when, a record that has to spill occupies fewer than 14 bytes in its slot (plus the page tail if it is the last record) - too little to host a FIRST_CHUNK header, which needs 1 marker + 4 size + 8 next-pointer. Everything else spills into chunks. The multi-page chunk chain arrived later (#332) and took over every case the placeholder used to serve except that one; MINIMUM_RECORD_SIZE's own comment records the overlap ("5 BYTES IS THE SPACE REQUIRED TO HOST THE PLACEHOLDER AND 1ST CHUCK FOR MULTI-PAGE CONTENT").
It is not dead code - Issue5279ConcurrentUpdateTest.aPlaceholderPointerRebuiltUnderConflictFallsBackAndStaysCorrect and Issue6129ChunkedSlotMergeTest.updatingPlaceholderContentCommutesWithItsPageNeighbours both produce one and assert totalPlaceholderRecords == 1 - but every route to it is that single 14-byte test.
The 14 bytes are usually there for the asking
The branch is reached because the page could not host the record's full new size. That says nothing about whether it has 14 bytes: growRecordInPage already shifts the records that follow to the right, and refuses only when
Getting a 9-byte slot up to 14 needs 5 more bytes, not 30 KB of them. A page that has just refused a large growth commonly has kilobytes free - in the #6129 test above, page 0 has roughly 1 KB free when the placeholder is created. The code simply never asks.
Proposal
Before falling back to the placeholder, try to grow the slot to the chunk-header minimum through the existing in-page shift, and spill into chunks. That reuses machinery already on this path, and leaves the placeholder reachable only when the page cannot spare ~13 bytes at all - if it can be shown to be unreachable, the whole RECORD_PLACEHOLDER_POINTER/RECORD_PLACEHOLDER_CONTENT family becomes removable (they are read in ~10 places across the scan, check, delete, update, compaction and space-accounting paths).
Records that outgrow their page would have exactly one representation instead of two, and every reader (check, scan, delete, compressPage, getPageOccupiedInBytes, getOrderedRecordsInPage) would lose a branch.
Backward compatibility is the obvious constraint: existing databases contain placeholders, so the readers have to stay until a format migration retires them. Only the writer would stop producing new ones.
Noticed while working on #6129 (PR #6140), where the placeholder turned out to be the least valuable of the three shapes that issue listed - and the one behind the still-open #6141.
Placeholders are still reachable, but they are the pre-chunk mechanism and there is exactly one way to get one
LocalBuckethas a single site that creates a placeholder pointer, in the "record must spill out of the page" branch ofupdateRecordInternal:So a placeholder happens when, and only when, a record that has to spill occupies fewer than 14 bytes in its slot (plus the page tail if it is the last record) - too little to host a
FIRST_CHUNKheader, which needs 1 marker + 4 size + 8 next-pointer. Everything else spills into chunks. The multi-page chunk chain arrived later (#332) and took over every case the placeholder used to serve except that one;MINIMUM_RECORD_SIZE's own comment records the overlap ("5 BYTES IS THE SPACE REQUIRED TO HOST THE PLACEHOLDER AND 1ST CHUCK FOR MULTI-PAGE CONTENT").It is not dead code -
Issue5279ConcurrentUpdateTest.aPlaceholderPointerRebuiltUnderConflictFallsBackAndStaysCorrectandIssue6129ChunkedSlotMergeTest.updatingPlaceholderContentCommutesWithItsPageNeighboursboth produce one and asserttotalPlaceholderRecords == 1- but every route to it is that single 14-byte test.The 14 bytes are usually there for the asking
The branch is reached because the page could not host the record's full new size. That says nothing about whether it has 14 bytes:
growRecordInPagealready shifts the records that follow to the right, and refuses only whenGetting a 9-byte slot up to 14 needs 5 more bytes, not 30 KB of them. A page that has just refused a large growth commonly has kilobytes free - in the #6129 test above, page 0 has roughly 1 KB free when the placeholder is created. The code simply never asks.
Proposal
Before falling back to the placeholder, try to grow the slot to the chunk-header minimum through the existing in-page shift, and spill into chunks. That reuses machinery already on this path, and leaves the placeholder reachable only when the page cannot spare ~13 bytes at all - if it can be shown to be unreachable, the whole
RECORD_PLACEHOLDER_POINTER/RECORD_PLACEHOLDER_CONTENTfamily becomes removable (they are read in ~10 places across the scan, check, delete, update, compaction and space-accounting paths).Why it is worth more than the code it deletes
check,scan,delete,compressPage,getPageOccupiedInBytes,getOrderedRecordsInPage) would lose a branch.Backward compatibility is the obvious constraint: existing databases contain placeholders, so the readers have to stay until a format migration retires them. Only the writer would stop producing new ones.