in core/src/main/java/com/jetbrains/youtrackdb/internal/core/storage/index/sbtree/singlevalue/v3/BTree.java [250:408]
private boolean update(
final AtomicOperation atomicOperation,
final K k,
final RID rid,
final IndexEngineValidator<K, RID> validator) {
return calculateInsideComponentOperation(
atomicOperation,
operation -> {
acquireExclusiveLock();
try {
var key = k;
var value = rid;
if (key != null) {
key = keySerializer.preprocess(serializerFactory, key, (Object[]) keyTypes);
final var serializedKey =
keySerializer.serializeNativeAsWhole(serializerFactory, key, (Object[]) keyTypes);
if (serializedKey.length > MAX_KEY_SIZE) {
throw new TooBigIndexKeyException(storage.getName(),
"Key size is more than allowed, operation was canceled. Current key size "
+ serializedKey.length
+ ", allowed "
+ MAX_KEY_SIZE, getName());
}
var bucketSearchResult =
findBucketForUpdate(key, atomicOperation);
var keyBucketCacheEntry =
loadPageForWrite(
atomicOperation, fileId, bucketSearchResult.getLastPathItem(), true);
var keyBucket =
new CellBTreeSingleValueBucketV3<K>(keyBucketCacheEntry);
final byte[] oldRawValue;
if (bucketSearchResult.getItemIndex() > -1) {
oldRawValue =
keyBucket.getRawValue(bucketSearchResult.getItemIndex(), keySerializer,
serializerFactory);
} else {
oldRawValue = null;
}
final RID oldValue;
if (oldRawValue == null) {
oldValue = null;
} else {
final int collectionId = ShortSerializer.INSTANCE.deserializeNative(oldRawValue, 0);
final var collectionPosition =
LongSerializer.INSTANCE.deserializeNative(
oldRawValue, ShortSerializer.SHORT_SIZE);
oldValue = new RecordId(collectionId, collectionPosition);
}
if (validator != null) {
var failure = true; // assuming validation throws by default
var ignored = false;
try {
final var result = validator.validate(key, oldValue, value);
if (result == IndexEngineValidator.IGNORE) {
ignored = true;
failure = false;
return false;
}
value = (RID) result;
failure = false;
} finally {
if (failure || ignored) {
keyBucketCacheEntry.close();
}
}
}
final var serializedValue =
new byte[ShortSerializer.SHORT_SIZE + LongSerializer.LONG_SIZE];
ShortSerializer.INSTANCE.serializeNative(
(short) value.getCollectionId(), serializedValue, 0);
LongSerializer.INSTANCE.serializeNative(
value.getCollectionPosition(), serializedValue, ShortSerializer.SHORT_SIZE);
int insertionIndex;
final int sizeDiff;
if (bucketSearchResult.getItemIndex() >= 0) {
assert oldRawValue != null;
if (oldRawValue.length == serializedValue.length) {
keyBucket.updateValue(
bucketSearchResult.getItemIndex(), serializedValue, serializedKey.length);
keyBucketCacheEntry.close();
return true;
} else {
keyBucket.removeLeafEntry(bucketSearchResult.getItemIndex(), serializedKey);
insertionIndex = bucketSearchResult.getItemIndex();
sizeDiff = 0;
}
} else {
insertionIndex = -bucketSearchResult.getItemIndex() - 1;
sizeDiff = 1;
}
while (!keyBucket.addLeafEntry(insertionIndex, serializedKey, serializedValue)) {
bucketSearchResult =
splitBucket(
keyBucket,
keyBucketCacheEntry,
bucketSearchResult.getPath(),
bucketSearchResult.getInsertionIndexes(),
insertionIndex,
atomicOperation);
insertionIndex = bucketSearchResult.getItemIndex();
final var pageIndex = bucketSearchResult.getLastPathItem();
if (pageIndex != keyBucketCacheEntry.getPageIndex()) {
keyBucketCacheEntry.close();
keyBucketCacheEntry = loadPageForWrite(atomicOperation, fileId, pageIndex, true);
}
//noinspection ObjectAllocationInLoop
keyBucket = new CellBTreeSingleValueBucketV3<>(keyBucketCacheEntry);
}
keyBucketCacheEntry.close();
if (sizeDiff != 0) {
updateSize(sizeDiff, atomicOperation);
}
} else {
var sizeDiff = 0;
final RID oldValue;
try (final var cacheEntry =
loadPageForWrite(atomicOperation, nullBucketFileId, 0, true)) {
final var nullBucket =
new CellBTreeSingleValueV3NullBucket(cacheEntry);
oldValue = nullBucket.getValue();
if (validator != null) {
final var result = validator.validate(null, oldValue, value);
if (result == IndexEngineValidator.IGNORE) {
return false;
}
}
if (oldValue != null) {
sizeDiff = -1;
}
nullBucket.setValue(value);
}
sizeDiff++;
updateSize(sizeDiff, atomicOperation);
}
return true;
} finally {
releaseExclusiveLock();
}
});
}