|
Search Solutions & Best Practices
Browse Solutions & Best Practices
|
composition-setup}
Summary: Lowering the Space Object Footprint - The Binary Serialization Pattern
Author: Shay Hasidim, Deputy CTO, GigaSpaces
OverviewBy default, when using the GigaSpace Java API, the space stores space object fields as is. No data compaction, or compression is done while the object is transported across the network or when stored within the space.
The GigaSpaces serialization API using the binary serialization technique allows you to reduce the footprint associated when storing space objects in memory. This means you will be able to store more space objects per memory unit. The basic idea of the binary serialization pattern is simple: Total control on the format of the space object data while transported over the network and when stored within the space. This technique:
The Basic FlowWith the binary serialization pattern:
When the object is written to the space:
When the object is read from the space:
The ImplementationUsing the binary serialization pattern can reduce the object footprint when stored within the space in drastic manner. As much as you will have more fields as part of the space object serialized using the GigaSpaces Serialization API, the memory footprint overhead will be smaller compared to the default serialization mode. The binary serialization pattern involves creation the following methods:
BinaryOutputStream and BinaryInputStreamThe BinaryOutputStream conatins various method to serialize all java's primitive type, their Object wrappers and arrays forms in a compacted mode. BinaryInputStream is its counterpart for deserialization. Your pack and unpack methods will be using an instance of those classes. ExampleWith the attached example we have a space class with 37 fields.
To run this example copy the example package zip into \GigaSpaces Root\examples\, extract the zip file and follow the instructions at the readme file. The Original Space classOur example involves a space class that will be modified to follow the binary serialization pattern. The original class includes:
The original class looks like this: @SpaceClass public class SimpleEntry { public SimpleEntry() { } private Integer _queryField; private Long _longFieldA1; .... @SpaceRouting @SpaceProperty(index=IndexType.BASIC) public Integer get_queryField() { return _queryField; } // getter and setter methods public void set_queryField(Integer field) { _queryField = field; } public Long get_longFieldA1() { return _longFieldA1; } public void set_longFieldA1(Long fieldA1) { _longFieldA1 = fieldA1; } The BinaryFormatEntry classThe modified class that implements the Binary serialization pattern includes:
The modified class looks like this: @SpaceClass(includeProperties=IncludeProperties.EXPLICIT) public class BinaryFormatEntry implements Externalizable { public BinaryFormatEntry(){} private Integer _queryField; private byte[] _binary; private Long _longFieldA1; .... @SpaceRouting @SpaceProperty(index=SpaceProperty.IndexType.BASIC) public Long getQueryField() { return _queryField; } public void setQueryField(Long queryField) { _queryField = queryField; } @SpaceProperty() public byte[] getBinary() { return _binary; } public void setBinary(byte[] _binary) { this._binary = _binary; } public Long get_longFieldA1() { return _longFieldA1; } public void set_longFieldA1(Long fieldA1) { _longFieldA1 = fieldA1; } ... public void pack(){...} public void unpack(){...} public void writeExternal(ObjectOutput out){...} public void readExternal(ObjectInput in) {...} private long getnulls(){...} private short checkNulls() {...} } The pack methodThe pack method serialize the object non indexed data. It is called explictly before calling the space write operation. public void pack() { BinaryOutputStream output = new BinaryOutputStream(); long nulls = getNulls(); output.writeLong(nulls); if (_longFieldA1 != null) output.writeLong(_longFieldA1); // ... etc. for all other compactable fields. _binary = output.toByteArray(); output.close(); } The unpack methodThis method de-serialize the object data by extracting the data from the byte array field and populating the fields with their corresponding values. Null values fields are non populated. This method is called after calling the space read operation. The Binar utility class is used to read the binary data and place it into the relevant field. public void unpack() { BinaryInputStream input = new BinaryInputStream(_binary); long nulls = input.readLong(); int i = 0; if ((nulls & 1L << i) == 0) _longFieldA1 = input.readLong(); i++; // ... etc. for all other compactable fields. input.close(); _binary = null; } The writeExternal methodThe writeExternal method serialize the object data into the output stream. public void writeExternal(ObjectOutput out) throws IOException { short nulls = 0; int i=0; nulls = checkNulls(); out.writeShort(nulls); if (_queryField != null) { out.writeLong(_queryField); } if (_binary != null) { out.write(_binary); } } The readExternal methodThe readExternal method essentially performs the opposite of the what the writeExternal method is doing. public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { short nulls; int i=0; nulls = in.readShort(); if( (nulls & 1L << i) == 0 ) _queryField = in.readLong(); i++; if( (nulls & 1L << i) == 0 ) { byte[] data = new byte[500]; int len = in.read(data); _binary = new byte[len]; System.arraycopy(data, 0, _binary, 0, len); } } The checkNulls methodThis method goes through the indexed fields and the byte array field and place into a short data type field an indication for the ones with null value using a bit map. private short checkNulls() { short nulls = 0; int i = 0; nulls = (short) ((_queryField == null) ? nulls | 1 << i : nulls); i++; nulls = (short) ((_binary == null) ? nulls | 1 << i : nulls); i++; return nulls; } The getnulls methodThis method goes through all class non indexed fields (the ones that their data is stored within the byte array) and place into a long data type field indication for the ones with null value using a bit map. private long getnulls() { long nulls = 0; int i=0; nulls = ((_longFieldA1 == null) ? nulls | 1L << i : nulls ) ; i++; nulls = ((_longFieldB1 == null) ? nulls | 1L << i : nulls ) ; i++; ... return nulls; } The Factory methodThe example using a factory method called generateBinaryFormatEntry to create the space object. Once it has been populated , its pack method is called. private BinaryFormatEntry generateBinaryFormatEntry(int id){ BinaryFormatEntry bfe = new BinaryFormatEntry(id, value1 , value2 ?) bfe.pack(); // the pack method is called implicitly as part of the factory method return bfe; } Writing and Reading the Object from the spaceThe following code snipped illustrates how the binary serialized object is written into the space and read from the space: GigaSpace _gigaspace; BinaryFormatEntry testBFE = generateBinaryFormatEntry(500); _gigaspace.write(testBFE, Lease.FOREVER); BinaryFormatEntry templateBFE = new BinaryFormatEntry(); templateBFE._queryField = new Long(500); BinaryFormatEntry resBFE = (BinaryFormatEntry)_gigaspace.read(templateBFE, 0); resBFE.unpack(); // this deserialize the binary data into the object fields ReferencesThe PackRat project allows you to use the Binary Serialization pattern via simple annotations. |
Additional resources: XAP Application Server | XAP Data Grid | XAP for Cloud Computing | XAP J2EE Support



Add Comment