Skip to content

Commit f8766c9

Browse files
Merge pull request #137 from mustafaiman/partitionAware
PartitionAware
2 parents 073717f + b121203 commit f8766c9

File tree

3 files changed

+132
-1
lines changed

3 files changed

+132
-1
lines changed

src/core/PartitionAware.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Implementing PartitionAware allows one to override the default partitioning scheme.
3+
* Instead of using the keys themselves to spread the data around the cluster the
4+
* key returned by {@link getPartitionKey} is used.
5+
* <p/>
6+
* This provides the user with an ability to contain related keys within the same
7+
* partition and, consequently, within the same node.
8+
* <p/>
9+
* In Hazelcast, disparate data structures will be stored on the same partition,
10+
* based on the partition key. For example, if "Steve" was used, then the following would be on one partition.
11+
* <ul>
12+
* <li>a customers IMap with an entry of key "Steve"</li>
13+
* <li>an orders IMap using a customer key type implementing PartitionAware with key "Steve</li>
14+
* <li>any queue named "Steve"</li>
15+
* <li>any PartitionAware object with partition key "Steve"</li>
16+
* </ul>
17+
*/
18+
export interface PartitionAware<T> {
19+
/**
20+
* The key that will be used by Hazelcast to specify the partition.
21+
* You should give the same key for objects that you want to be in the same partition.
22+
*/
23+
getPartitionKey(): T;
24+
}

src/serialization/SerializationService.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {PortableSerializer} from './portable/PortableSerializer';
1414
import {IdentifiedDataSerializableFactory} from './Serializable';
1515
import * as DefaultPredicates from './DefaultPredicates';
1616
import {PredicateFactory, PREDICATE_FACTORY_ID} from './PredicateFactory';
17+
import {PartitionAware} from '../core/PartitionAware';
1718

1819
export interface SerializationService {
1920
toData(object: any, paritioningStrategy?: any) : Data;
@@ -50,7 +51,14 @@ export class SerializationServiceV1 implements SerializationService {
5051
toData(object: any, partitioningStrategy: any = this.defaultPartitionStrategy): Data {
5152
var dataOutput: DataOutput = new PositionalObjectDataOutput(1, this, this.serialiationConfig.isBigEndian);
5253
var serializer = this.findSerializerFor(object);
53-
dataOutput.writeIntBE(this.calculatePartitionHash(object, partitioningStrategy));
54+
//Check if object is partition aware
55+
if (object != null && object.getPartitionKey) {
56+
var partitionKey = object.getPartitionKey();
57+
var serializedPartitionKey = this.toData(partitionKey);
58+
dataOutput.writeIntBE(this.calculatePartitionHash(serializedPartitionKey, partitioningStrategy));
59+
} else {
60+
dataOutput.writeIntBE(this.calculatePartitionHash(object, partitioningStrategy));
61+
}
5462
dataOutput.writeIntBE(serializer.getId());
5563
serializer.write(dataOutput, object);
5664
return new HeapData(dataOutput.toBuffer());

test/map/MapPartitionAwareTest.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
var Client = require('../../.').Client;
2+
var Controller = require('../RC');
3+
var expect = require('chai').expect;
4+
var Promise = require('bluebird');
5+
6+
describe('Map Partition Aware', function() {
7+
8+
var cluster;
9+
var numOfEntries = 10000;
10+
var memberCount = 3;
11+
var members = [];
12+
var client;
13+
var map;
14+
var mapName = 'testMap';
15+
16+
function PartitionAwareKey(key, partitionKey) {
17+
this.key = key;
18+
this.partitionKey = partitionKey;
19+
}
20+
21+
PartitionAwareKey.prototype.getPartitionKey = function() {
22+
return this.partitionKey;
23+
};
24+
25+
function getLocalMapStats(serverInstance) {
26+
return 'function getLocalMapStats() {' +
27+
' var map = instance_' + serverInstance + '.getMap("' + mapName + '");' +
28+
' return map.getLocalMapStats().getOwnedEntryCount();' +
29+
'}' +
30+
'result=""+getLocalMapStats();';
31+
}
32+
33+
function _fillMap(map, ssize) {
34+
var entryList = [];
35+
for (var i = 0; i < ssize; i++) {
36+
entryList.push([new PartitionAwareKey(''+Math.random(), 'specificKey'), ''+Math.random()]);
37+
}
38+
return map.putAll(entryList);
39+
}
40+
41+
before(function() {
42+
expect(memberCount, 'This test should have at least 2 members.').to.be.at.least(2);
43+
this.timeout(20000);
44+
return Controller.createCluster(null, null).then(function(c) {
45+
cluster = c;
46+
for (var i = 0; i < memberCount; i++) {
47+
members.push(Controller.startMember(cluster.id));
48+
}
49+
return Promise.all(members);
50+
}).then(function(m) {
51+
members = m;
52+
return Client.newHazelcastClient();
53+
}).then(function(cl) {
54+
client = cl;
55+
});
56+
});
57+
58+
after(function() {
59+
this.timeout(20000);
60+
client.shutdown();
61+
return Controller.shutdownCluster(cluster.id);
62+
});
63+
64+
beforeEach(function() {
65+
map = client.getMap(mapName);
66+
});
67+
68+
afterEach(function() {
69+
return map.destroy();
70+
});
71+
72+
it('put', function() {
73+
this.timeout(10000);
74+
return _fillMap(map, numOfEntries).then(function(newVal) {
75+
var promises = members.map(function(member, index) {
76+
return Controller.executeOnController(cluster.id, getLocalMapStats(index), 1);
77+
});
78+
return Promise.all(promises);
79+
}).then(function(stats) {
80+
var entriesPerMember = stats.map(function(item) {
81+
return Number(item.result);
82+
});
83+
var expectedArray = [numOfEntries];
84+
for (var i = 0; i < memberCount - 1; i++) {
85+
expectedArray.push(0);
86+
}
87+
return expect(entriesPerMember, 'One member should have all of the entries. The rest will have 0 entries.').to.have.members(expectedArray);
88+
});
89+
});
90+
91+
it('get', function() {
92+
var key = new PartitionAwareKey('key', 'partKey');
93+
return map.put(key, 'value').then(function() {
94+
return map.get(key);
95+
}).then(function(val) {
96+
return expect(val).to.equal('value');
97+
});
98+
})
99+
});

0 commit comments

Comments
 (0)