diff --git a/evita_engine/src/main/java/io/evitadb/core/buffer/DataStoreMemoryBuffer.java b/evita_engine/src/main/java/io/evitadb/core/buffer/DataStoreMemoryBuffer.java index f709726e5..d840b6187 100644 --- a/evita_engine/src/main/java/io/evitadb/core/buffer/DataStoreMemoryBuffer.java +++ b/evita_engine/src/main/java/io/evitadb/core/buffer/DataStoreMemoryBuffer.java @@ -29,7 +29,6 @@ import io.evitadb.index.EntityIndex; import io.evitadb.index.Index; import io.evitadb.index.IndexKey; -import io.evitadb.store.exception.CompressionKeyUnknownException; import io.evitadb.store.model.StoragePart; import io.evitadb.store.service.KeyCompressor; import io.evitadb.store.spi.CatalogPersistenceService; @@ -38,6 +37,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Objects; +import java.util.OptionalLong; import java.util.function.BiFunction; import java.util.function.Function; @@ -167,24 +167,19 @@ public byte[] fetchBinary(long catalogVersion, long prim * is not opened) reads it from the target {@link CatalogPersistenceService}. */ @Nullable - public > T fetch(long catalogVersion, @Nonnull U originalKey, @Nonnull Class containerType, @Nonnull BiFunction compressedKeyComputer) { - final DataStoreChanges layer = getTransactionalMemoryLayerIfExists(transactionalMemoryDataSource); - if (layer == null) { - try { - final long storagePartId = compressedKeyComputer.apply(this.persistenceService.getReadOnlyKeyCompressor(), originalKey); - return persistenceService.getStoragePart(catalogVersion, storagePartId, containerType); - } catch (CompressionKeyUnknownException ex) { - // key wasn't yet assigned - return null; - } + public > T fetch(long catalogVersion, @Nonnull U originalKey, @Nonnull Class containerType, @Nonnull BiFunction compressedKeyComputer) { + final DataStoreChanges layer = getTransactionalMemoryLayerIfExists(this.transactionalMemoryDataSource); + final OptionalLong storagePartId = compressedKeyComputer.apply( + layer == null ? this.persistenceService.getReadOnlyKeyCompressor() : layer.getReadOnlyKeyCompressor(), + originalKey + ); + if (storagePartId.isEmpty()) { + // key wasn't yet assigned + return null; } else { - try { - final long storagePartId = compressedKeyComputer.apply(layer.getReadOnlyKeyCompressor(), originalKey); - return layer.getStoragePart(catalogVersion, storagePartId, containerType); - } catch (CompressionKeyUnknownException ex) { - // key wasn't yet assigned - return null; - } + return layer == null ? + this.persistenceService.getStoragePart(catalogVersion, storagePartId.getAsLong(), containerType) : + layer.getStoragePart(catalogVersion, storagePartId.getAsLong(), containerType); } } @@ -193,24 +188,19 @@ public > T fetch(long catalogVers * is not opened) reads it from the target {@link CatalogPersistenceService}. */ @Nullable - public > byte[] fetchBinary(long catalogVersion, @Nonnull U originalKey, @Nonnull Class containerType, @Nonnull BiFunction compressedKeyComputer) { + public > byte[] fetchBinary(long catalogVersion, @Nonnull U originalKey, @Nonnull Class containerType, @Nonnull BiFunction compressedKeyComputer) { final DataStoreChanges layer = getTransactionalMemoryLayerIfExists(transactionalMemoryDataSource); - if (layer == null) { - try { - final long nonFlushedCompressedId = compressedKeyComputer.apply(this.persistenceService.getReadOnlyKeyCompressor(), originalKey); - return persistenceService.getStoragePartAsBinary(catalogVersion, nonFlushedCompressedId, containerType); - } catch (CompressionKeyUnknownException ex) { - // key wasn't yet assigned - return null; - } + final OptionalLong storagePartId = compressedKeyComputer.apply( + layer == null ? this.persistenceService.getReadOnlyKeyCompressor() : layer.getReadOnlyKeyCompressor(), + originalKey + ); + if (storagePartId.isEmpty()) { + // key wasn't yet assigned + return null; } else { - try { - final long nonFlushedCompressedId = compressedKeyComputer.apply(layer.getReadOnlyKeyCompressor(), originalKey); - return layer.getStoragePartAsBinary(catalogVersion, nonFlushedCompressedId, containerType); - } catch (CompressionKeyUnknownException ex) { - // key wasn't yet assigned - return null; - } + return layer == null ? + this.persistenceService.getStoragePartAsBinary(catalogVersion, storagePartId.getAsLong(), containerType) : + layer.getStoragePartAsBinary(catalogVersion, storagePartId.getAsLong(), containerType); } } diff --git a/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/AggregatedKeyCompressor.java b/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/AggregatedKeyCompressor.java index 9c2f0cc7b..bbf81b0f8 100644 --- a/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/AggregatedKeyCompressor.java +++ b/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/AggregatedKeyCompressor.java @@ -30,6 +30,7 @@ import javax.annotation.Nullable; import java.io.Serial; import java.util.Map; +import java.util.OptionalInt; /** * The AggregatedKeyCompressor class implements the KeyCompressor interface and represents a compressor that combines @@ -57,11 +58,11 @@ public > int getId(@Nonnull T key) throws CompressionKey // Iterate over all compressors for (KeyCompressor compressor : compressors) { // Try to get the ID of the key from the current compressor - Integer id = compressor.getIdIfExists(key); + final OptionalInt id = compressor.getIdIfExists(key); // If the ID is not null, return it - if (id != null) { - return id; + if (id.isPresent()) { + return id.getAsInt(); } } @@ -69,22 +70,22 @@ public > int getId(@Nonnull T key) throws CompressionKey throw new CompressionKeyUnknownException("Key not found: " + key); } - @Nullable + @Nonnull @Override - public > Integer getIdIfExists(@Nonnull T key) { + public > OptionalInt getIdIfExists(@Nonnull T key) { // Iterate over all compressors for (KeyCompressor compressor : compressors) { // Try to get the ID of the key from the current compressor - Integer id = compressor.getIdIfExists(key); + final OptionalInt id = compressor.getIdIfExists(key); // If the ID is not null, return it - if (id != null) { + if (id.isPresent()) { return id; } } // If no compressor has the key, return null - return null; + return OptionalInt.empty(); } @Nonnull diff --git a/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/ReadOnlyKeyCompressor.java b/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/ReadOnlyKeyCompressor.java index 5dac1037b..11f8d590b 100644 --- a/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/ReadOnlyKeyCompressor.java +++ b/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/ReadOnlyKeyCompressor.java @@ -35,6 +35,8 @@ import java.util.Collections; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; +import java.util.OptionalInt; import static io.evitadb.utils.CollectionUtils.createHashMap; @@ -76,20 +78,22 @@ public ReadOnlyKeyCompressor(Map keys) { @Override public @Nonnull Map getKeys() { - return idToKeyIndex; + return this.idToKeyIndex; } @Override public > int getId(@Nonnull T key) { - final Integer id = keyToIdIndex.get(key); + final Integer id = this.keyToIdIndex.get(key); Assert.isPremiseValid(id != null, () -> new CompressionKeyUnknownException("There is no id for key " + key + "!")); return id; } - @Nullable + @Nonnull @Override - public > Integer getIdIfExists(@Nonnull T key) { - return keyToIdIndex.get(key); + public > OptionalInt getIdIfExists(@Nonnull T key) { + return Optional.ofNullable(keyToIdIndex.get(key)) + .map(OptionalInt::of) + .orElseGet(OptionalInt::empty); } @Nonnull diff --git a/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/ReadWriteKeyCompressor.java b/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/ReadWriteKeyCompressor.java index dc82929a3..fee211f8b 100644 --- a/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/ReadWriteKeyCompressor.java +++ b/evita_store/evita_store_common/src/main/java/io/evitadb/store/compressor/ReadWriteKeyCompressor.java @@ -33,6 +33,8 @@ import java.io.Serial; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; +import java.util.OptionalInt; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -103,10 +105,12 @@ public > int getId(@Nonnull T key) { }); } - @Nullable + @Nonnull @Override - public > Integer getIdIfExists(@Nonnull T key) { - return keyToIdIndex.get(key); + public > OptionalInt getIdIfExists(@Nonnull T key) { + return Optional.ofNullable(this.keyToIdIndex.get(key)) + .map(OptionalInt::of) + .orElseGet(OptionalInt::empty); } @Nonnull diff --git a/evita_store/evita_store_common/src/main/java/io/evitadb/store/service/KeyCompressor.java b/evita_store/evita_store_common/src/main/java/io/evitadb/store/service/KeyCompressor.java index 40e1afa01..a23c1043b 100644 --- a/evita_store/evita_store_common/src/main/java/io/evitadb/store/service/KeyCompressor.java +++ b/evita_store/evita_store_common/src/main/java/io/evitadb/store/service/KeyCompressor.java @@ -33,6 +33,7 @@ import javax.annotation.Nullable; import java.io.Serializable; import java.util.Map; +import java.util.OptionalInt; import static io.evitadb.api.requestResponse.data.AssociatedDataContract.AssociatedDataKey; @@ -67,8 +68,8 @@ public interface KeyCompressor extends Serializable { * * Method may return null when no id exists yet and the implementation cannot generate new id. */ - @Nullable - > Integer getIdIfExists(@Nonnull T key); + @Nonnull + > OptionalInt getIdIfExists(@Nonnull T key); /** * Returns original `key` that is linked to passed integer id that was acquired during deserialization from Kryo. diff --git a/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/entity/AssociatedDataStoragePart.java b/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/entity/AssociatedDataStoragePart.java index 05d92a585..7ae7678ea 100644 --- a/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/entity/AssociatedDataStoragePart.java +++ b/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/entity/AssociatedDataStoragePart.java @@ -44,6 +44,7 @@ import java.io.Serializable; import java.util.Locale; import java.util.OptionalInt; +import java.util.OptionalLong; import static io.evitadb.utils.ComparatorUtils.compareLocale; @@ -67,6 +68,10 @@ public class AssociatedDataStoragePart implements EntityStoragePart, RecordWithC * See {@link AssociatedDataValue#key()}. */ private final EntityAssociatedDataKey associatedDataKey; + /** + * Contains information about size of this container in bytes. + */ + private final int sizeInBytes; /** * Id used for lookups in persistent storage for this particular container. */ @@ -75,15 +80,35 @@ public class AssociatedDataStoragePart implements EntityStoragePart, RecordWithC * See {@link AssociatedDataValue#value()}. */ @Getter private AssociatedDataValue value; - /** - * Contains information about size of this container in bytes. - */ - private final int sizeInBytes; /** * Contains true if anything changed in this container. */ @Getter private boolean dirty; + /** + * Computes primary ID of this container that is a long consisting of two parts: + * - int entity primary key + * - int key assigned by {@link KeyCompressor} for its {@link AssociatedDataKey} + */ + @Nonnull + public static OptionalLong computeUniquePartId(@Nonnull KeyCompressor keyCompressor, @Nonnull EntityAssociatedDataKey key) { + final OptionalInt id = keyCompressor.getIdIfExists( + new AssociatedDataKey( + key.associatedDataName(), key.locale() + ) + ); + if (id.isPresent()) { + return OptionalLong.of( + NumberUtils.join( + key.entityPrimaryKey(), + id.getAsInt() + ) + ); + } else { + return OptionalLong.empty(); + } + } + public AssociatedDataStoragePart(int entityPrimaryKey, @Nonnull AssociatedDataKey associatedDataKey) { this.storagePartPK = null; this.entityPrimaryKey = entityPrimaryKey; @@ -99,22 +124,6 @@ public AssociatedDataStoragePart(long storagePartPK, int entityPrimaryKey, @Nonn this.sizeInBytes = sizeInBytes; } - /** - * Computes primary ID of this container that is a long consisting of two parts: - * - int entity primary key - * - int key assigned by {@link KeyCompressor} for its {@link AssociatedDataKey} - */ - public static long computeUniquePartId(@Nonnull KeyCompressor keyCompressor, @Nonnull EntityAssociatedDataKey key) { - return NumberUtils.join( - key.entityPrimaryKey(), - keyCompressor.getId( - new AssociatedDataKey( - key.associatedDataName(), key.locale() - ) - ) - ); - } - @Override public EntityAssociatedDataKey getStoragePartSourceKey() { return associatedDataKey; @@ -123,8 +132,16 @@ public EntityAssociatedDataKey getStoragePartSourceKey() { @Override public long computeUniquePartIdAndSet(@Nonnull KeyCompressor keyCompressor) { Assert.isTrue(this.storagePartPK == null, "Unique part id is already known!"); - Assert.notNull(entityPrimaryKey, "Entity primary key must be non-null!"); - this.storagePartPK = AssociatedDataStoragePart.computeUniquePartId(keyCompressor, associatedDataKey); + Assert.notNull(this.entityPrimaryKey, "Entity primary key must be non-null!"); + this.storagePartPK = NumberUtils.join( + this.associatedDataKey.entityPrimaryKey(), + keyCompressor.getId( + new AssociatedDataKey( + this.associatedDataKey.associatedDataName(), + this.associatedDataKey.locale() + ) + ) + ); return this.storagePartPK; } diff --git a/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/entity/AttributesStoragePart.java b/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/entity/AttributesStoragePart.java index 2119c5789..43392c166 100644 --- a/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/entity/AttributesStoragePart.java +++ b/evita_store/evita_store_entity/src/main/java/io/evitadb/store/entity/model/entity/AttributesStoragePart.java @@ -54,6 +54,7 @@ import java.util.Arrays; import java.util.Locale; import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.function.UnaryOperator; /** @@ -83,6 +84,10 @@ public class AttributesStoragePart implements EntityStoragePart, RecordWithCompr * Contains key for attribute set lookup. */ private final EntityAttributesSetKey attributeSetKey; + /** + * Contains information about size of this container in bytes. + */ + private final int sizeInBytes; /** * Id used for lookups in persistent storage for this particular container. */ @@ -91,10 +96,6 @@ public class AttributesStoragePart implements EntityStoragePart, RecordWithCompr * See {@link Attributes#getAttributeValues()}. Attributes are sorted in ascending order according to {@link AttributeKey}. */ @Getter private AttributeValue[] attributes = EMPTY_ATTRIBUTE_VALUES; - /** - * Contains information about size of this container in bytes. - */ - private final int sizeInBytes; /** * Contains true if anything changed in this container. */ @@ -107,13 +108,21 @@ public class AttributesStoragePart implements EntityStoragePart, RecordWithCompr * * @throws CompressionKeyUnknownException when key is not recognized by {@link KeyCompressor} */ - public static long computeUniquePartId(@Nonnull KeyCompressor keyCompressor, @Nonnull EntityAttributesSetKey attributeSetKey) throws CompressionKeyUnknownException { - return NumberUtils.join( - attributeSetKey.entityPrimaryKey(), - keyCompressor.getId( - new AttributesSetKey(attributeSetKey.locale()) - ) + @Nonnull + public static OptionalLong computeUniquePartId(@Nonnull KeyCompressor keyCompressor, @Nonnull EntityAttributesSetKey attributeSetKey) throws CompressionKeyUnknownException { + final OptionalInt id = keyCompressor.getIdIfExists( + new AttributesSetKey(attributeSetKey.locale()) ); + if (id.isPresent()) { + return OptionalLong.of( + NumberUtils.join( + attributeSetKey.entityPrimaryKey(), + id.getAsInt() + ) + ); + } else { + return OptionalLong.empty(); + } } public AttributesStoragePart(int entityPrimaryKey) { @@ -146,8 +155,13 @@ public EntityAttributesSetKey getStoragePartSourceKey() { @Override public long computeUniquePartIdAndSet(@Nonnull KeyCompressor keyCompressor) { Assert.isTrue(this.storagePartPK == null, "Unique part id is already known!"); - Assert.notNull(entityPrimaryKey, "Entity primary key must be non null!"); - this.storagePartPK = computeUniquePartId(keyCompressor, attributeSetKey); + Assert.notNull(this.entityPrimaryKey, "Entity primary key must be non null!"); + this.storagePartPK = NumberUtils.join( + this.attributeSetKey.entityPrimaryKey(), + keyCompressor.getId( + new AttributesSetKey(this.attributeSetKey.locale()) + ) + ); return this.storagePartPK; } diff --git a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java index 297dbb8ac..d3e1e5010 100644 --- a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java +++ b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/DefaultEntityCollectionPersistenceService.java @@ -247,7 +247,7 @@ entityPrimaryKey, null, new AssociatedDataValueSerializablePredicate(evitaReques priceStorageContainer ), ioFetchStatistics.getIoFetchCount(), - ioFetchStatistics.ioFetchedBytes + ioFetchStatistics.getIoFetchedBytes() ); } diff --git a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/model/MutableCatalogEntityHeader.java b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/model/MutableCatalogEntityHeader.java index e6776f36b..2d2088b3c 100644 --- a/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/model/MutableCatalogEntityHeader.java +++ b/evita_store/evita_store_server/src/main/java/io/evitadb/store/catalog/model/MutableCatalogEntityHeader.java @@ -34,6 +34,8 @@ import java.io.Serial; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; +import java.util.OptionalInt; import java.util.concurrent.atomic.AtomicInteger; import static io.evitadb.utils.CollectionUtils.createHashMap; @@ -100,10 +102,12 @@ public > int getId(@Nonnull T key) { }); } - @Nullable + @Nonnull @Override - public > Integer getIdIfExists(@Nonnull T key) { - return keyToIdIndex.get(key); + public > OptionalInt getIdIfExists(@Nonnull T key) { + return Optional.ofNullable(keyToIdIndex.get(key)) + .map(OptionalInt::of) + .orElseGet(OptionalInt::empty); } @Nonnull @@ -129,7 +133,6 @@ public int hashCode() { result = 31 * result + idToKeyIndex.hashCode(); result = 31 * result + keyToIdIndex.hashCode(); result = 31 * result + Integer.hashCode(keySequence.get()); - result = 31 * result + recordCount; return result; } @@ -140,7 +143,6 @@ public boolean equals(Object o) { MutableCatalogEntityHeader that = (MutableCatalogEntityHeader) o; - if (recordCount != that.recordCount) return false; if (!entityType.equals(that.entityType)) return false; if (!idToKeyIndex.equals(that.idToKeyIndex)) return false; if (!keyToIdIndex.equals(that.keyToIdIndex)) return false;