G
Property Storage Adapters
Overview
If you need to transform data (for example, compress or encrypt the value of a property) when it is copied from the user object to the Space, or vice versa, one way is to apply a storage adapter.
GigaSpaces provides a Property Storage Adapter API to create and denote a storage adapter for a property, as well as several general-purpose, built-in adapters that you can use out of the box, or extend to suit your needs.
The following built-in adapters are available:
- BinaryAdapter - stores values in binary serialized format.
- ZipAdapter - applies compression/decompression using the Zip algorithm.
- AesCbcAdapter/AesGcmAdapter - encrypts/decrypts values using AES.
These adapters are described in more detail later on this page.
Applying a Property Storage Adapter
Use the @SpacePropertyStorageAdapter
annotation to specify a storage adapter for a property. For example, suppose you have a class called Message
with a string property called text
, and you want to compress it before storing it in the Space, and decompress it when retrieving it. You can use the ZipAdapter built-in adapter as follows:
public class Message {
// Additional properties and metadata are omitted for brevity
private String text;
@SpacePropertyStorageAdapter(ZipAdapter.class)
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Alternatively, if you’re using a Space document and need to introduce the type explicitly, do the following:
SpaceTypeDescriptor typeDesc = new SpaceTypeDescriptorBuilder("Message")
// Additional properties and metadata are omitted for brevity
.addFixedProperty("text", String.class, ZipAdapter.class)
.create();
Queries and Indexes
Querying Transformed Data
Storage adapters usually transform the property value, so naive matching of the query parameters against the Space value will yield incorrect results. For query execution to work correctly, the following conditions must exist:
-
The query parameters should undergo the same transformation as the property values. This is handled by the GigaSpaces query executor.
-
The transformation should be deterministic.
For security reasons, the encryption adapter transformation is non-deterministic by default.
-
For equality matching, the stored value should override
hashcode()
andequals()
. GigaSpaces provides a binary wrapper to facilitate storing byte arrays such as compressed or encrypted data. -
For ordered matching, the stored value should preserve the original ordering.
Compression and encryption don't support ordered matching.
When executing a query, the GigaSpaces query executor checks each property’s storage adapter (if any). If the storage adapter supports the query operation (equality/order), it performs the transformation, otherwise it throws an exception with an explanatory error message.
Each storage adapter indicates whether it supports matching using the supportsEqualsMatching()
and supportsOrderedMatching()
methods.
Indexing Property Storage Adapters
A property storage adapter can be indexed if the storage adapter supports the relevant matching. If the matching isn't supported, an exception is thrown with the appropriate error message.
Built-In Storage Adapters
The following storage adapters are provided with GigaSpaces.
You can extend the built-in storage adapters as needed, or add your own custom storage adapter as explained further down on this page.
Compression Storage Adapters
BinaryAdapter
The BinaryAdapter stores property values in binary serialized form. This has the following benefits:
- Schema evolution support - The property is not deserialized in the Space, which allows users to incorporate application-specific schema evolution.
- Smaller footprint - For some values/types, storing data in serialized form can reduce the memory footprint.
- Lower CPU requirements - Values are stored in serialized form, so there’s no need to deserialize/re-serialize them when writing/reading from the Space.
ZipAdapter
The ZipAdapter compresses/decompresses properties using the Zip algorithm. The benefits of this storage adapter are the similar to those of the BinaryAdapter, with the following differences:
- Smallest footprint - The memory footprint will likely be smaller than what is achieved with the BinaryAdapter storage adapter, depending on how well the value can be compressed.
- Higher CPU requirements - This storage adapter requires more CPU resources to perform compression/decompression.
Encryption (AES) Storage Adapters
The encryption storage adapters provide encryption/decryption using the AES block cipher algorithm, which requires choosing a block cipher mode. GigaSpaces supports two common block cipher modes; CBC via the AesCbcAdapter, and GCM via the AesGcmAdapter.
Key/Passphrase
Even a strong encryption algorithm is vulnerable if the encryption key is leaked, and the question of how to store and protect this key is a common dilemma when dealing with encryption.
The GigaSpaces AES adapters derive the encryption key (and authentication key, if needed) from a passphrase provided by the user. The passphrase can be provided using one of the following properties:
com.gs.property-storage.aes.passphrase
- provides the passphrase directly.com.gs.property-storage.aes.passphrase-path
- provides the path to a file containing the passphrase.
As we know, storing secrets in system properties is not recommended because of the ease with which they can be read from another process. As such, the passphrase
property should be used for only for testing purposes. The passphrase-path
property is more secure, assuming the access to the path is restricted. Alternatively, if you have a more secure means for storing and retrieving the passphrase, you can override the getPassPhrase()
method to retrieve the passphrase accordingly.
Matching
As noted above, the GigaSpaces encryption storage adapters don't support matching by default for security reasons. A common rule of thumb in security is that encrypting the same plaintext multiple times produces different ciphertext each time, to prevent attackers from obtaining the information they need to crack the encryption or gain access to sensitive data.
For example, let's say that you have a Space class with a password
property , and you encrypt it using an AES storage adapter. If the encryption is deterministic, an attacker can query the Space and request entries that are secured with password “123456”, and receive those entries, etc. In order to prevent this scenario, the AES algorithm scrambles the encryption with an additional component called an initialization vector, which is randomly generated.
If your use case requires matching encrypted content, and the potential risk inherent with deterministic encryption is acceptable, you can extend the relevant GigaSpaces encyption adapter (preferably CBC, because GCM is more vulnerable to non-unique initialization vectors), and do the following:
- Override
supportsEqualsMatching
and set it totrue
. - Override
generateInitializationVector(int length)
and generate a non-random initialization vector (either empty or fixed).
Co-Located Execution
Storage adapters are triggered by the Space proxy. But in co-located execution (namely Processing Units or Space tasks), the Space proxy is co-located with the Space on the server side, which may not have access to the encryption key. If this is the case, you can't write or read encrypted data. You can, however, use projections to read unencrypted properties, and perform change operations to change unencrypted properties.
Implementing a Custom Storage Adapter
To implement a custom storage adapter, you can create a class that extends the PropertyStorageAdapter
. There are two abstract methods that you have to implement (toSpace
and fromSpace
), and everything else is optional.
Use the following guidelines for implementing a custom storage adapter:
- The
PropertyStorageAdapter
base class contains numerous helper methods that you may find useful in implementingtoSpace
/fromSpace
, such as serialization, compression, and binary wrappers. The GigaSpaces adapters also use those methods, which are open source and can therefore be freely reviewed and researched. - You can override the
getName()
method to provide a user-friendly name, which is displayed in the GigaSpaces operational tools. - Custom query support is disabled by default in the GigaSpaces property storage adapters. If your implementation supports queries, you can enable it as described above in the Queries and Indexes section.
- If your environment includes MemoryXtend, you can override the
getStorageClass()
method to specify the class of the values stored in the Space, so that MemoryXtend can optimize its storage and reduce the footprint.
Replacing the GigaSpaces StorageType
If you’re currently using the GigaSpaces Storage Types feature and want to use a property storage adapter instead, use the following guidelines to migrate between storage methods:
- Replace
@SpaceStorageType(storageType=StorageType.BINARY)
with@SpacePropertyStorageAdapter(BinaryAdapter.class)
- Replace
@SpaceStorageType(storageType=StorageType.COMPRESSED)
with@SpacePropertyStorageAdapter(ZipAdapter.class)
StorageType
is not supported for primitive or String properties. GigaSpaces storage adapters are generic and therefore not limited to specific types, but in some cases implementing an adapter may not provide any benefit (for example, compressing short strings may result in longer strings after transformation).- StorageType can be set on classes, and then applied only to properties of that class with supported type. The GigaSpaces storage adapters don't have supported types but instead are set on properties, so they can't be set on the class level.
Limitations
- JDBC is currently not supported; the property storage adapters are not invoked when executing queries.
- The GigaSpaces Change API is not supported because the property storage adapters are invoked on the client side but changes are applied on the server side.