Skip to content

Commit 05164bb

Browse files
fix: Fix deathlock during statically registering services (#4657)
Signed-off-by: Pavel Jareš <Pavel.Jares@broadcom.com> Co-authored-by: Jakub Balhar <jakub@balhar.net>
1 parent ad2769a commit 05164bb

2 files changed

Lines changed: 159 additions & 326 deletions

File tree

discovery-service/src/main/java/org/zowe/apiml/discovery/ApimlInstanceRegistry.java

Lines changed: 40 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,7 @@
3131

3232
import java.lang.invoke.MethodHandle;
3333
import java.lang.invoke.MethodHandles;
34-
import java.lang.invoke.MethodType;
35-
import java.lang.invoke.WrongMethodTypeException;
36-
import java.lang.reflect.Constructor;
3734
import java.lang.reflect.Field;
38-
import java.lang.reflect.InvocationTargetException;
3935
import java.lang.reflect.Method;
4036
import java.util.*;
4137
import java.util.concurrent.ConcurrentHashMap;
@@ -55,13 +51,6 @@ public class ApimlInstanceRegistry extends InstanceRegistry {
5551

5652
private static final String EXCEPTION_MESSAGE = "Implementation of InstanceRegistry changed, please verify fix of order sending events";
5753

58-
private MethodHandle handleRegistrationMethod;
59-
private MethodHandle handlerResolveInstanceLeaseDurationMethod;
60-
private MethodHandle handleCancellationMethod;
61-
62-
private MethodHandle register2ArgsMethodHandle;
63-
private MethodHandle register3ArgsMethodHandle;
64-
private MethodHandle cancelMethodHandle;
6554
private MethodHandle replicateToPeersMethodHandle;
6655

6756
private final ApplicationContext appCntx;
@@ -70,6 +59,8 @@ public class ApimlInstanceRegistry extends InstanceRegistry {
7059
private ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry;
7160
private Set<String> staticRegistrationIds = Collections.synchronizedSet(new HashSet<>());
7261

62+
private static final ThreadLocal<Integer> RENEW_CORRECTION = new ThreadLocal<>();
63+
7364
public ApimlInstanceRegistry(
7465
EurekaServerConfig serverConfig,
7566
EurekaClientConfig clientConfig,
@@ -96,79 +87,29 @@ public ApimlInstanceRegistry(
9687
*/
9788
private void init() {
9889
try {
99-
Method registrationMethod =
100-
InstanceRegistry.class.getDeclaredMethod("handleRegistration",
101-
InstanceInfo.class, int.class, boolean.class
102-
);
103-
registrationMethod.setAccessible(true);
104-
handleRegistrationMethod = MethodHandles.lookup().unreflect(registrationMethod);
105-
106-
Method cancelationMethod =
107-
InstanceRegistry.class.getDeclaredMethod("handleCancelation",
108-
String.class, String.class, boolean.class
109-
);
110-
cancelationMethod.setAccessible(true);
111-
handleCancellationMethod = MethodHandles.lookup().unreflect(cancelationMethod);
112-
113-
Method resolveInstanceLeaseDurationMethod =
114-
InstanceRegistry.class.getDeclaredMethod("resolveInstanceLeaseDuration",
115-
InstanceInfo.class
116-
);
117-
resolveInstanceLeaseDurationMethod.setAccessible(true);
118-
handlerResolveInstanceLeaseDurationMethod = MethodHandles.lookup().unreflect(resolveInstanceLeaseDurationMethod);
119-
120-
Constructor<MethodHandles.Lookup> lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
121-
lookupConstructor.setAccessible(true);
122-
MethodHandles.Lookup lookup = lookupConstructor.newInstance(PeerAwareInstanceRegistryImpl.class);
123-
124-
register2ArgsMethodHandle =
125-
lookup.findSpecial(
126-
PeerAwareInstanceRegistryImpl.class,
127-
"register",
128-
MethodType.methodType(void.class, InstanceInfo.class, boolean.class),
129-
PeerAwareInstanceRegistryImpl.class
130-
);
131-
132-
cancelMethodHandle =
133-
lookup.findSpecial(
134-
PeerAwareInstanceRegistryImpl.class,
135-
"cancel",
136-
MethodType.methodType(boolean.class, String.class, String.class, boolean.class),
137-
PeerAwareInstanceRegistryImpl.class
138-
);
139-
140-
lookup = lookupConstructor.newInstance(AbstractInstanceRegistry.class);
141-
142-
register3ArgsMethodHandle =
143-
lookup.findSpecial(
144-
AbstractInstanceRegistry.class,
145-
"register",
146-
MethodType.methodType(void.class, InstanceInfo.class, int.class, boolean.class),
147-
AbstractInstanceRegistry.class
148-
);
14990
Field registryField = AbstractInstanceRegistry.class.getDeclaredField("registry");
15091
registryField.setAccessible(true);
15192
this.registry = (ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>) registryField.get(this);
15293

15394
Method replicateToPeers = PeerAwareInstanceRegistryImpl.class.getDeclaredMethod("replicateToPeers", Action.class, String.class, String.class, InstanceInfo.class, InstanceStatus.class, boolean.class);
15495
replicateToPeers.setAccessible(true);
155-
15696
replicateToPeersMethodHandle = MethodHandles.lookup().unreflect(replicateToPeers);
157-
} catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
97+
} catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException e) {
15898
throw new IllegalArgumentException(EXCEPTION_MESSAGE, e);
15999
}
160100
}
161101

162-
protected int resolveInstanceLeaseDurationRewritten(final InstanceInfo info) {
163-
try {
164-
return (int) handlerResolveInstanceLeaseDurationMethod.invokeWithArguments(this, info);
165-
} catch (ClassCastException | WrongMethodTypeException e) {
166-
throw new IllegalArgumentException(EXCEPTION_MESSAGE, e);
167-
} catch (RuntimeException re) {
168-
throw re;
169-
} catch (Throwable t) {
170-
throw new IllegalArgumentException(EXCEPTION_MESSAGE, t);
102+
@Override
103+
protected void updateRenewsPerMinThreshold() {
104+
Integer correction = RENEW_CORRECTION.get();
105+
if (correction != null) {
106+
synchronized (lock) {
107+
this.expectedNumberOfClientsSendingRenews += correction;
108+
}
109+
RENEW_CORRECTION.remove();
171110
}
111+
112+
super.updateRenewsPerMinThreshold();
172113
}
173114

174115
public void peerAwareHeartbeat(InstanceInfo instanceInfo) {
@@ -191,19 +132,17 @@ public void registerStatically(InstanceInfo instanceInfo, boolean isReplication,
191132
// the maximum lease duration time (Eureka bug: overflow of int during conversion to ms)
192133
int leaseDuration = Integer.MAX_VALUE / 1000;
193134

194-
// temporary register (do not increase count of service to avoid threshold)
195-
synchronized (lock) {
196-
int backup = expectedNumberOfClientsSendingRenews;
197-
try {
198-
register(instanceInfo, leaseDuration, isReplication);
199-
if (peerReplicate) {
200-
replicateToPeersMethodHandle.invokeWithArguments(this, Action.Register, instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null, isReplication);
201-
}
202-
} catch (Throwable e) {
203-
throw new IllegalStateException(EXCEPTION_MESSAGE, e);
204-
} finally {
205-
expectedNumberOfClientsSendingRenews = backup;
135+
try {
136+
// temporary register (do not increase count of service to avoid threshold)
137+
RENEW_CORRECTION.set(-1);
138+
register(instanceInfo, leaseDuration, isReplication);
139+
if (peerReplicate) {
140+
replicateToPeersMethodHandle.invokeWithArguments(this, Action.Register, instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null, isReplication);
206141
}
142+
} catch (Throwable e) {
143+
throw new IllegalStateException(EXCEPTION_MESSAGE, e);
144+
} finally {
145+
RENEW_CORRECTION.remove();
207146
}
208147

209148
// register lease plan to never expired
@@ -229,34 +168,18 @@ public boolean isExpired(long additionalLeaseMs) {
229168
*/
230169
@Override
231170
public void register(InstanceInfo info, int leaseDuration, boolean isReplication) {
232-
validateInstanceInfo(info);
233-
info = changeServiceId(info);
234-
try {
235-
register3ArgsMethodHandle.invokeWithArguments(this, info, leaseDuration, isReplication);
236-
handleRegistrationMethod.invokeWithArguments(this, info, leaseDuration, isReplication);
237-
} catch (ClassCastException | WrongMethodTypeException e) {
238-
throw new IllegalArgumentException(EXCEPTION_MESSAGE, e);
239-
} catch (RuntimeException re) {
240-
throw re;
241-
} catch (Throwable t) {
242-
throw new IllegalArgumentException(EXCEPTION_MESSAGE, t);
243-
}
171+
validateInstanceInfo(info);
172+
info = changeServiceId(info);
173+
174+
super.register(info, leaseDuration, isReplication);
244175
}
245176

246177
@Override
247178
public void register(InstanceInfo info, final boolean isReplication) {
248-
validateInstanceInfo(info);
249-
info = changeServiceId(info);
250-
try {
251-
register2ArgsMethodHandle.invokeWithArguments(this, info, isReplication);
252-
handleRegistrationMethod.invokeWithArguments(this, info, resolveInstanceLeaseDurationRewritten(info), isReplication);
253-
} catch (ClassCastException | WrongMethodTypeException e) {
254-
throw new IllegalArgumentException(EXCEPTION_MESSAGE, e);
255-
} catch (RuntimeException re) {
256-
throw re;
257-
} catch (Throwable t) {
258-
throw new IllegalArgumentException(EXCEPTION_MESSAGE, t);
259-
}
179+
validateInstanceInfo(info);
180+
info = changeServiceId(info);
181+
182+
super.register(info, isReplication);
260183
}
261184

262185
/**
@@ -309,25 +232,16 @@ public boolean isRegisterable(InstanceInfo instanceInfo) {
309232

310233
@Override
311234
public boolean cancel(String appName, String serverId, boolean isReplication) {
312-
synchronized (lock) {
313-
int backup = expectedNumberOfClientsSendingRenews;
314-
try {
315-
String[] updatedValues = replaceValues(appName, serverId);
316-
final boolean out = (boolean) cancelMethodHandle.invokeWithArguments(this, updatedValues[0], updatedValues[1], isReplication);
317-
handleCancellationMethod.invokeWithArguments(this, updatedValues[0], updatedValues[1], isReplication);
318-
return out;
319-
} catch (ClassCastException | WrongMethodTypeException e) {
320-
throw new IllegalArgumentException(EXCEPTION_MESSAGE, e);
321-
} catch (RuntimeException re) {
322-
throw re;
323-
} catch (Throwable t) {
324-
throw new IllegalArgumentException(EXCEPTION_MESSAGE, t);
325-
} finally {
326-
if (staticRegistrationIds.removeAll(Optional.ofNullable(registry.get(appName)).orElse(Collections.emptyMap()).keySet())) {
327-
// do not change count of instances if it was registered statically
328-
expectedNumberOfClientsSendingRenews = backup;
329-
}
235+
try {
236+
String[] updatedValues = replaceValues(appName, serverId);
237+
238+
if (staticRegistrationIds.removeAll(Optional.ofNullable(registry.get(appName)).orElse(Collections.emptyMap()).keySet())) {
239+
// do not change count of instances if it was registered statically
240+
RENEW_CORRECTION.set(1);
330241
}
242+
return super.cancel(updatedValues[0], updatedValues[1], isReplication);
243+
} finally {
244+
RENEW_CORRECTION.remove();
331245
}
332246
}
333247

0 commit comments

Comments
 (0)