Skip to content

Commit ec810c4

Browse files
yagmurdulgermdumandag
authored andcommitted
Portable versioning documentation (#177)
* Portable versioning documentation. Fixed spacing errors from previous commit * Fixed required changes for portable versioning documentation * Fixed with required grammatical and content changes. * Fixed grammatical error and md style
1 parent 68afba0 commit ec810c4

1 file changed

Lines changed: 85 additions & 5 deletions

File tree

README.md

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* [1.4.1. Configuring Hazelcast IMDG](#141-configuring-hazelcast-imdg)
1313
* [1.4.2. Configuring Hazelcast Python Client](#142-configuring-hazelcast-python-client)
1414
* [1.4.2.1. Group Settings](#1421-group-settings)
15-
* [1.4.2.2. Network Settings](#1421-network-settings)
15+
* [1.4.2.2. Network Settings](#1422-network-settings)
1616
* [1.4.3. Client System Properties](#143-client-system-properties)
1717
* [1.5. Basic Usage](#15-basic-usage)
1818
* [1.6. Code Samples](#16-code-samples)
@@ -23,6 +23,7 @@
2323
* [4. Serialization](#4-serialization)
2424
* [4.1. IdentifiedDataSerializable Serialization](#41-identifieddataserializable-serialization)
2525
* [4.2. Portable Serialization](#42-portable-serialization)
26+
* [4.2.1. Versioning for Portable Serialization](#421-versioning-for-portable-serialization)
2627
* [4.3. Custom Serialization](#43-custom-serialization)
2728
* [4.4. JSON Serialization](#44-json-serialization)
2829
* [4.5. Global Serialization](#45-global-serialization)
@@ -766,7 +767,7 @@ As an alternative to the existing serialization methods, Hazelcast offers portab
766767

767768
In order to support these features, a serialized `Portable` object contains meta information like the version and concrete location of the each field in the binary data. This way Hazelcast is able to navigate in the binary data and deserialize only the required field without actually deserializing the whole object which improves the query performance.
768769

769-
With multiversion support, you can have two members where each of them having different versions of the same object, and Hazelcast will store both meta information and use the correct one to serialize and deserialize portable objects depending on the member. This is very helpful when you are doing a rolling upgrade without shutting down the cluster.
770+
With multiversion support, you can have two members each having different versions of the same object; Hazelcast stores both meta information and uses the correct one to serialize and deserialize portable objects depending on the member. This is very helpful when you are doing a rolling upgrade without shutting down the cluster.
770771

771772
Also note that portable serialization is totally language independent and is used as the binary protocol between Hazelcast server and clients.
772773

@@ -776,14 +777,18 @@ A sample portable implementation of a `Foo` class looks like the following:
776777
from hazelcast.serialization.api import Portable
777778

778779
class Foo(Portable):
780+
781+
CLASS_ID = 1
782+
FACTORY_ID = 1
783+
779784
def __init__(self, foo=None):
780785
self.foo = foo
781786

782787
def get_class_id(self):
783-
return 1
788+
return CLASS_ID
784789

785790
def get_factory_id(self):
786-
return 1
791+
return FACTORY_ID
787792

788793
def write_portable(self, writer):
789794
writer.write_utf("foo", self.foo)
@@ -792,7 +797,7 @@ class Foo(Portable):
792797
self.foo = reader.read_utf("foo")
793798
```
794799

795-
> Note: For Portable to work in Python client, the class that inherits it should have default valued parameters in its `__init__` method so that an instance of that class can be created without passing any arguments to it.
800+
> **NOTE: For Portable to work in Python client, the class that inherits it should have default valued parameters in its `__init__` method so that an instance of that class can be created without passing any arguments to it.**
796801
797802
Similar to `IdentifiedDataSerializable`, a `Portable` class must provide the `get_class_id()` and `get_factory_id()` methods. The factory dictionary will be used to create the `Portable` object given the class ID.
798803

@@ -812,6 +817,81 @@ config.serialization_config.data_serializable_factories[1] = factory
812817

813818
Note that the ID that is passed to the `SerializationConfig` is same as the factory ID that `Foo` class returns.
814819

820+
### 4.2.1. Versioning for Portable Serialization
821+
822+
More than one version of the same class may need to be serialized and deserialized. For example, a client may have an older version of a class and the member to which it is connected may have a newer version of the same class.
823+
824+
Portable serialization supports versioning. It is a global versioning, meaning that all portable classes that are serialized through a member get the globally configured portable version.
825+
826+
You can declare the version in the `hazelcast.xml` configuration file using the `portable-version` element, as shown below.
827+
828+
```xml
829+
<hazelcast>
830+
...
831+
<serialization>
832+
<portable-version>1</portable-version>
833+
</serialization>
834+
...
835+
</hazelcast>
836+
```
837+
838+
If you update the class by changing the type of one of the fields or by adding a new field, it is a good idea to upgrade the version of the class, rather than sticking to the global version specified in the `hazelcast.xml` file.
839+
In the Python client, you can achieve this by simply adding the `get_class_version()` method to your class’s implementation of `Portable`, and setting the `CLASS_VERSION` to be different than the default global version.
840+
841+
> **NOTE: If you do not use the `get_class_version()` method in your `Portable` implementation, it will have the global version, by default.**
842+
843+
Here is an example implementation of creating a version 2 for the above Foo class:
844+
845+
```python
846+
from hazelcast.serialization.api import Portable
847+
848+
class Foo(Portable):
849+
850+
CLASS_ID = 1
851+
FACTORY_ID = 1
852+
CLASS_VERSION = 2
853+
854+
def __init__(self, foo=None, foo2=None):
855+
self.foo = foo
856+
self.foo2 = foo2
857+
858+
def get_class_id(self):
859+
return CLASS_ID
860+
861+
def get_factory_id(self):
862+
return FACTORY_ID
863+
864+
def get_class_version(self):
865+
return CLASS_VERSION
866+
867+
def write_portable(self, writer):
868+
writer.write_utf("foo", self.foo)
869+
writer.write_utf("foo2", self.foo2)
870+
871+
def read_portable(self, reader):
872+
self.foo = reader.read_utf("foo")
873+
self.foo2 = reader.read_utf("foo2")
874+
```
875+
876+
You should consider the following when you perform versioning:
877+
878+
* It is important to change the version whenever an update is performed in the serialized fields of a class, for example by incrementing the version.
879+
* If a client performs a Portable deserialization on a field and then that Portable is updated by removing that field on the cluster side, this may lead to problems such as an AttributeError being raised when an older version of the client tries to access the removed field.
880+
* Portable serialization does not use reflection and hence, fields in the class and in the serialized content are not automatically mapped. Field renaming is a simpler process. Also, since the class ID is stored, renaming the Portable does not lead to problems.
881+
* Types of fields need to be updated carefully. Hazelcast performs basic type upgradings, such as `int` to `float`.
882+
883+
#### Example Portable Versioning Scenarios:
884+
885+
Assume that a new client joins to the cluster with a class that has been modified and class's version has been upgraded due to this modification.
886+
887+
If you modified the class by adding a new field, the new client’s put operations include that new field. If this new client tries to get an object that was put from the older clients, it gets null for the newly added field.
888+
889+
If you modified the class by removing a field, the old clients get null for the objects that are put by the new client.
890+
891+
If you modified the class by changing the type of a field to an incompatible type (such as from `int` to `String`), a `TypeError` (wrapped as `HazelcastSerializationError`) is generated as the client tries accessing an object with the older version of the class. The same applies if a client with the old version tries to access a new version object.
892+
893+
If you did not modify a class at all, it works as usual.
894+
815895
## 4.3. Custom Serialization
816896

817897
Hazelcast lets you plug a custom serializer to be used for serialization of objects.

0 commit comments

Comments
 (0)