Skip to content

Commit 062cd77

Browse files
metanetmdogan
authored andcommitted
Improve RaftLock and RaftSemaphore idempotency
Results of lock/acquire and unlock/release operations are memorized per <session, thread>. It is because we know that each <session, thread> pair will have at most 1 active operation at a time and it will not send a new operation before it is done with the current operation. Therefore, we memorize result of the last operation for each <session id, thread> pair and gc them on session close.
1 parent 962be76 commit 062cd77

File tree

13 files changed

+301
-297
lines changed

13 files changed

+301
-297
lines changed

hazelcast-raft-dataservices/src/main/java/com/hazelcast/raft/service/blocking/BlockingResource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ protected BlockingResource(RaftGroupId groupId, String name) {
4949
this.name = name;
5050
}
5151

52-
public abstract Collection<Long> getOwnerSessionIds();
52+
public abstract Collection<Long> getActiveSessions();
5353

5454
public final RaftGroupId getGroupId() {
5555
return groupId;

hazelcast-raft-dataservices/src/main/java/com/hazelcast/raft/service/blocking/ResourceRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public final Map<W, Tuple2<Long, Long>> getWaitTimeouts() {
151151
public final Collection<Long> getActiveSessions() {
152152
Set<Long> sessions = new HashSet<Long>();
153153
for (R res : resources.values()) {
154-
sessions.addAll(res.getOwnerSessionIds());
154+
sessions.addAll(res.getActiveSessions());
155155
for (WaitKey key : res.getWaitKeys()) {
156156
sessions.add(key.sessionId());
157157
}

hazelcast-raft-dataservices/src/main/java/com/hazelcast/raft/service/countdownlatch/CountDownLatchRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
public class CountDownLatchRegistry extends ResourceRegistry<CountDownLatchInvocationKey, RaftCountDownLatch>
3131
implements IdentifiedDataSerializable {
3232

33-
public CountDownLatchRegistry() {
33+
CountDownLatchRegistry() {
3434
}
3535

3636
CountDownLatchRegistry(RaftGroupId groupId) {

hazelcast-raft-dataservices/src/main/java/com/hazelcast/raft/service/countdownlatch/RaftCountDownLatch.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ public class RaftCountDownLatch extends BlockingResource<CountDownLatchInvocatio
4444
private int countDownFrom;
4545
private final Set<UUID> countDownUids = new HashSet<UUID>();
4646

47-
public RaftCountDownLatch() {
47+
RaftCountDownLatch() {
4848
}
4949

5050
RaftCountDownLatch(RaftGroupId groupId, String name) {
5151
super(groupId, name);
5252
}
5353

5454
@Override
55-
public Collection<Long> getOwnerSessionIds() {
55+
public Collection<Long> getActiveSessions() {
5656
return Collections.emptyList();
5757
}
5858

hazelcast-raft-dataservices/src/main/java/com/hazelcast/raft/service/lock/LockRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
*/
3232
class LockRegistry extends ResourceRegistry<LockInvocationKey, RaftLock> implements IdentifiedDataSerializable {
3333

34-
public LockRegistry() {
34+
LockRegistry() {
3535
}
3636

3737
LockRegistry(RaftGroupId groupId) {

hazelcast-raft-dataservices/src/main/java/com/hazelcast/raft/service/lock/RaftLock.java

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
import java.util.ArrayList;
2929
import java.util.Collection;
3030
import java.util.Collections;
31+
import java.util.HashMap;
3132
import java.util.Iterator;
3233
import java.util.List;
34+
import java.util.Map;
3335
import java.util.UUID;
3436

3537
import static com.hazelcast.raft.service.lock.RaftLockService.INVALID_FENCE;
@@ -42,37 +44,41 @@ class RaftLock extends BlockingResource<LockInvocationKey> implements Identified
4244

4345
private LockInvocationKey owner;
4446
private int lockCount;
45-
private UUID releaseRefUid;
47+
private Map<LockEndpoint, UUID> invocationRefUids = new HashMap<LockEndpoint, UUID>();
4648

47-
public RaftLock() {
49+
RaftLock() {
4850
}
4951

5052
RaftLock(RaftGroupId groupId, String name) {
5153
super(groupId, name);
5254
}
5355

5456
@Override
55-
public Collection<Long> getOwnerSessionIds() {
57+
public Collection<Long> getActiveSessions() {
5658
return owner != null ? Collections.singleton(owner.sessionId()) : Collections.<Long>emptyList();
5759
}
5860

5961
AcquireResult acquire(LockEndpoint endpoint, long commitIndex, UUID invocationUid, boolean wait) {
6062
// if acquire() is being retried
61-
if (owner != null && owner.invocationUid().equals(invocationUid)) {
63+
if (invocationUid.equals(invocationRefUids.get(endpoint))
64+
|| (owner != null && owner.invocationUid().equals(invocationUid))) {
6265
return AcquireResult.successful(owner.commitIndex());
6366
}
6467

68+
invocationRefUids.remove(endpoint);
69+
6570
LockInvocationKey key = new LockInvocationKey(name, endpoint, commitIndex, invocationUid);
6671
if (owner == null) {
6772
owner = key;
6873
}
6974

7075
if (endpoint.equals(owner.endpoint())) {
76+
invocationRefUids.put(endpoint, invocationUid);
7177
lockCount++;
7278
return AcquireResult.successful(owner.commitIndex());
7379
}
7480

75-
Collection<LockInvocationKey> cancelledWaitKeys = cancelWaitKeys(endpoint, invocationUid);
81+
Collection<LockInvocationKey> cancelledWaitKeys = cancelWaitKeys(endpoint);
7682

7783
if (wait) {
7884
waitKeys.add(key);
@@ -81,12 +87,12 @@ AcquireResult acquire(LockEndpoint endpoint, long commitIndex, UUID invocationUi
8187
return AcquireResult.failed(cancelledWaitKeys);
8288
}
8389

84-
private Collection<LockInvocationKey> cancelWaitKeys(LockEndpoint endpoint, UUID invocationUid) {
90+
private Collection<LockInvocationKey> cancelWaitKeys(LockEndpoint endpoint) {
8591
List<LockInvocationKey> cancelled = new ArrayList<LockInvocationKey>(0);
8692
Iterator<LockInvocationKey> it = waitKeys.iterator();
8793
while (it.hasNext()) {
8894
LockInvocationKey waitKey = it.next();
89-
if (waitKey.endpoint().equals(endpoint) && !waitKey.invocationUid().equals(invocationUid)) {
95+
if (waitKey.endpoint().equals(endpoint)) {
9096
cancelled.add(waitKey);
9197
it.remove();
9298
}
@@ -101,12 +107,12 @@ ReleaseResult release(LockEndpoint endpoint, UUID invocationUuid) {
101107

102108
private ReleaseResult release(LockEndpoint endpoint, int releaseCount, UUID invocationUid) {
103109
// if release() is being retried
104-
if (invocationUid.equals(releaseRefUid)) {
110+
if (invocationUid.equals(invocationRefUids.get(endpoint))) {
105111
return ReleaseResult.SUCCESSFUL;
106112
}
107113

108114
if (owner != null && endpoint.equals(owner.endpoint())) {
109-
releaseRefUid = invocationUid;
115+
invocationRefUids.put(endpoint, invocationUid);
110116

111117
lockCount -= Math.min(releaseCount, lockCount);
112118
if (lockCount > 0) {
@@ -115,39 +121,21 @@ private ReleaseResult release(LockEndpoint endpoint, int releaseCount, UUID invo
115121

116122
LockInvocationKey newOwner = waitKeys.poll();
117123
if (newOwner != null) {
118-
List<LockInvocationKey> keys = new ArrayList<LockInvocationKey>();
119-
keys.add(newOwner);
120-
121-
Iterator<LockInvocationKey> iter = waitKeys.iterator();
122-
while (iter.hasNext()) {
123-
LockInvocationKey key = iter.next();
124-
if (newOwner.invocationUid().equals(key.invocationUid())) {
125-
assert newOwner.endpoint().equals(key.endpoint());
126-
keys.add(key);
127-
iter.remove();
128-
}
129-
}
130-
131124
owner = newOwner;
132125
lockCount = 1;
133126

134-
return ReleaseResult.successful(keys);
127+
return ReleaseResult.successful(Collections.singleton(newOwner));
135128
} else {
136129
owner = null;
137130
}
138131

139132
return ReleaseResult.SUCCESSFUL;
140133
}
141134

142-
return ReleaseResult.failed(cancelWaitKeys(endpoint, invocationUid));
135+
return ReleaseResult.failed(cancelWaitKeys(endpoint));
143136
}
144137

145138
ReleaseResult forceRelease(long expectedFence, UUID invocationUid) {
146-
// if forceRelease() is being retried
147-
if (invocationUid.equals(releaseRefUid)) {
148-
return ReleaseResult.SUCCESSFUL;
149-
}
150-
151139
if (owner == null) {
152140
return ReleaseResult.FAILED;
153141
}
@@ -170,6 +158,13 @@ LockInvocationKey owner() {
170158
@Override
171159
protected void onInvalidateSession(long sessionId, Long2ObjectHashMap<Object> responses) {
172160
if (owner != null && sessionId == owner.endpoint().sessionId()) {
161+
Iterator<LockEndpoint> it = invocationRefUids.keySet().iterator();
162+
while (it.hasNext()) {
163+
if (it.next().sessionId() == sessionId) {
164+
it.remove();
165+
}
166+
}
167+
173168
ReleaseResult result = release(owner.endpoint(), Integer.MAX_VALUE, UuidUtil.newUnsecureUUID());
174169

175170
if (!result.success) {
@@ -207,11 +202,12 @@ public void writeData(ObjectDataOutput out)
207202
out.writeObject(owner);
208203
}
209204
out.writeInt(lockCount);
210-
boolean hasRefUid = (releaseRefUid != null);
211-
out.writeBoolean(hasRefUid);
212-
if (hasRefUid) {
213-
out.writeLong(releaseRefUid.getLeastSignificantBits());
214-
out.writeLong(releaseRefUid.getMostSignificantBits());
205+
out.writeInt(invocationRefUids.size());
206+
for (Map.Entry<LockEndpoint, UUID> e : invocationRefUids.entrySet()) {
207+
out.writeObject(e.getKey());
208+
UUID releaseUid = e.getValue();
209+
out.writeLong(releaseUid.getLeastSignificantBits());
210+
out.writeLong(releaseUid.getMostSignificantBits());
215211
}
216212
}
217213

@@ -224,18 +220,19 @@ public void readData(ObjectDataInput in)
224220
owner = in.readObject();
225221
}
226222
lockCount = in.readInt();
227-
boolean hasRefUid = in.readBoolean();
228-
if (hasRefUid) {
223+
int releaseRefUidCount = in.readInt();
224+
for (int i = 0; i < releaseRefUidCount; i++) {
225+
LockEndpoint endpoint = in.readObject();
229226
long least = in.readLong();
230227
long most = in.readLong();
231-
releaseRefUid = new UUID(most, least);
228+
invocationRefUids.put(endpoint, new UUID(most, least));
232229
}
233230
}
234231

235232
@Override
236233
public String toString() {
237234
return "RaftLock{" + "groupId=" + groupId + ", name='" + name + '\'' + ", owner=" + owner + ", lockCount=" + lockCount
238-
+ ", releaseRefUid=" + releaseRefUid + ", waitKeys=" + waitKeys + '}';
235+
+ ", invocationRefUids=" + invocationRefUids + ", waitKeys=" + waitKeys + '}';
239236
}
240237

241238
static class AcquireResult {

0 commit comments

Comments
 (0)