...
ID | IEP-97 | ||||||||
Author | Anton Vinogradov | ||||||||
Sponsor | |||||||||
Created |
| ||||||||
Status |
|
...
...
Code Block | ||||
---|---|---|---|---|
| ||||
protected byte[] valueBytesFromValue(CacheObjectValueContext ctx) throws IgniteCheckedException { byte[] bytes = ctx.kernalContext().cacheObjects().marshal(ctx, val); return CacheObjectTransformerCacheObjectTransformerUtils.transformIfNecessary(bytes, ctx); } protected Object valueFromValueBytes(CacheObjectValueContext ctx, ClassLoader ldr) throws IgniteCheckedException { byte[] bytes = CacheObjectTransformerCacheObjectTransformerUtils.restoreIfNecessary(valBytes, ctx); return ctx.kernalContext().cacheObjects().unmarshal(ctx, bytes, ldr); } public void prepareMarshal(CacheObjectValueContext ctx) throws IgniteCheckedException { if (valBytes == null) valBytes = valueBytesFromValue(ctx); } public void finishUnmarshal(CacheObjectValueContext ctx, ClassLoader ldr) throws IgniteCheckedException { if (val == null) val = valueFromValueBytes(ctx, ldr); } |
...
It's not possible to just replace arr with valBytes because, unlike , for example, from CacheObjectImpl arr is not just a mashalled bytes, it's an object's value that is required, for example, to provide hashCode/schemaId/typeId/objectField, and we must keep it as is.
...
Code Block | ||||
---|---|---|---|---|
| ||||
private byte[] arrayFromValueBytes(CacheObjectValueContext ctx) { return CacheObjectTransformerCacheObjectTransformerUtils.restoreIfNecessary(valBytes, ctx); } private byte[] valueBytesFromArray(CacheObjectValueContext ctx) { return CacheObjectTransformerCacheObjectTransformerUtils.transformIfNecessary(arr, start, arr.length, ctx); } public void finishUnmarshal(CacheObjectValueContext ctx, ClassLoader ldr) throws IgniteCheckedException { if (arr == null) arr = arrayFromValueBytes(ctx); } public void prepareMarshal(CacheObjectValueContext ctx) { if (valBytes == null) valBytes = valueBytesFromArray(ctx); } |
...
Some customers may want to encrypt the data, some to compress it, while some just keep it as is.
So, we must provide a simple way to append any transformation.
...
Code Block | ||||
---|---|---|---|---|
| ||||
public interface CacheObjectTransformerSpiCacheObjectTransformerManager extends IgniteSpi { /** Additional space required to store the transformed data. */ public int OVERHEAD = 6; GridCacheSharedManager { /** * Transforms the data. * * @param bytes Byte array contains the data. * @param offset Data offset. * @param length Data lengthoriginal Original data. * @return Byte array contains the transformedTransformed data started with non-filled area(started with {@link #OVERHEADGridBinaryMarshaller#TRANSFORMED} when size.restorable) * or @throws{@code IgniteCheckedExceptionnull} when transformation is not possible/suitable. */ public @Nullable byte[]ByteBuffer transform(byte[] bytes, int offset, int length) throws IgniteCheckedException; ByteBuffer original); /** * Restores the data. * * @param bytes Byte array ending with the transformed data. * @param offset Transformed data offset. * @return Byte array contains the restoredRestored data. */ public byte[]ByteBuffer restore(byte[] bytes, int offsetByteBuffer transformed); } |
This API is known for the overhead used to store transformed data and is able to work with byte arrays with custom offsets, which is necessary to guarantee performance.
Every customer may implement this interface in a proper way if necessary and specify it in the via plugin configuration:
Code Block | ||||
---|---|---|---|---|
| ||||
IgniteConfiguration getConfiguration() { IgniteConfiguration cfg = ... cfg.setCacheObjectTransformerSpisetPluginProviders(new XXXTransformerSpiXXXPluginProvider()); return // Which provides some XXXCacheObjectTransformerManager() return cfg; } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
public abstract class CacheObjectTransformerSpiAdapterCompressionTransformer extends IgniteSpiAdapterCacheObjectTransformerAdapter implements CacheObjectTransformerSpi { ... /** * Transforms the data. * * @param original Original data. * @return Transformed data. * @throws IgniteCheckedException when transformation is not possible/suitable. */ protected abstract{ protected ByteBuffer transform(ByteBuffer original) throws IgniteCheckedException; { /** * Restores the data. * int overhead = 5; // *Transformed @paramflag transformed+ Transformed datalength. * @return Restored data. */ int protectedorigSize abstract ByteBuffer restore(ByteBuffer transformed= original.remaining(); } |
Code Block | ||||
---|---|---|---|---|
| ||||
class CompressionTransformerSpi extends CacheObjectTransformerSpiAdapter { protected ByteBuffer transform(ByteBuffer original) throws IgniteCheckedException { int lim = original.remaining()origSize - CacheObjectTransformerSpi.OVERHEADoverhead; if (lim <= 0) return thrownull; new// IgniteCheckedException("Compression is not profitable."); ByteBuffer compressed = byteBuffer(lim);(overhead + (int)Zstd.compressBound(origSize)); int origSize = original.remaining(compressed.put(TRANSFORMED); compressed.positionputInt(locOverheadorigSize); int size = Zstd.compress(compressed, original, 1); if (size >= lim) compressed.flip(); compressed.putInt(origSize); return null; // Compression is not profitable. compressed.rewindflip(); return compressed; } protected ByteBuffer restore(ByteBuffer transformed) { ByteBuffer restored = byteBuffer(transformed.getInt()); Zstd.decompress(restored, transformed); restored.flip(); return restored; } } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
class EncryptionTransformerSpiEncryptionTransformer extends CacheObjectTransformerSpiAdapterCacheObjectTransformerAdapter { private static final int SHIFT = 42; // Secret! protected ByteBuffer transform(ByteBuffer original) throws IgniteCheckedException { ByteBuffer transformed = byteBuffer(original.remaining() + 1); // Same capacity is required. transformed.put(TRANSFORMED); while (original.hasRemaining()) transformed.put((byte)(original.get() + SHIFT)); transformed.flip(); return transformed; } protected ByteBuffer restore(ByteBuffer transformed, int length) { ByteBuffer restored = byteBuffer(transformed.remaining()); // Same size. while (transformed.hasRemaining()) restored.put((byte)(transformed.get() - SHIFT)); restored.flip(); return restored; } } |
...