Skip to content

Commit f68cdcb

Browse files
committed
fix #4924: small performance refinements to crud mock
1 parent 85ce40f commit f68cdcb

File tree

5 files changed

+41
-32
lines changed

5 files changed

+41
-32
lines changed

junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/KubernetesCrudDispatcher.java

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
import java.util.Set;
4949
import java.util.concurrent.CopyOnWriteArraySet;
5050
import java.util.concurrent.atomic.AtomicLong;
51+
import java.util.concurrent.locks.ReadWriteLock;
52+
import java.util.concurrent.locks.ReentrantReadWriteLock;
5153

5254
public class KubernetesCrudDispatcher extends CrudDispatcher implements KubernetesCrudPersistence, CustomResourceAware {
5355

@@ -60,6 +62,7 @@ public class KubernetesCrudDispatcher extends CrudDispatcher implements Kubernet
6062
private final KubernetesCrudDispatcherHandler postHandler;
6163
private final KubernetesCrudDispatcherHandler putHandler;
6264
private final KubernetesCrudDispatcherHandler patchHandler;
65+
private final ReadWriteLock lock = new ReentrantReadWriteLock();
6366

6467
public KubernetesCrudDispatcher() {
6568
this(Collections.emptyList());
@@ -81,12 +84,13 @@ public KubernetesCrudDispatcher(List<CustomResourceDefinitionContext> crdContext
8184
}
8285

8386
MockResponse process(RecordedRequest request, KubernetesCrudDispatcherHandler handler) {
84-
synchronized (map) {
85-
try {
86-
return handler.handle(request);
87-
} catch (KubernetesCrudDispatcherException e) {
88-
return new MockResponse().setResponseCode(e.getCode()).setBody(e.toStatusBody());
89-
}
87+
lock.writeLock().lock();
88+
try {
89+
return handler.handle(request);
90+
} catch (KubernetesCrudDispatcherException e) {
91+
return new MockResponse().setResponseCode(e.getCode()).setBody(e.toStatusBody());
92+
} finally {
93+
lock.writeLock().unlock();
9094
}
9195
}
9296

@@ -120,11 +124,14 @@ public MockResponse handleUpdate(RecordedRequest request) {
120124
*/
121125
@Override
122126
public MockResponse handleGet(String path) {
123-
synchronized (map) {
127+
lock.readLock().lock();
128+
try {
124129
if (detectWatchMode(path)) {
125130
return handleWatch(path);
126131
}
127132
return handle(path, null);
133+
} finally {
134+
lock.readLock().unlock();
128135
}
129136
}
130137

@@ -188,28 +195,26 @@ public MockResponse handlePatch(RecordedRequest request) {
188195
*/
189196
@Override
190197
public MockResponse handleDelete(String path) {
191-
synchronized (map) {
198+
lock.writeLock().lock();
199+
try {
192200
return handle(path, this::processDelete);
201+
} finally {
202+
lock.writeLock().unlock();
193203
}
194204
}
195205

196206
private void processDelete(String path, AttributeSet pathAttributes, AttributeSet oldAttributes) {
197207
String jsonStringOfResource = map.get(oldAttributes);
198-
/*
199-
* Potential performance improvement: The resource is unmarshalled and marshalled in other places (e.g., when creating a
200-
* WatchEvent later).
201-
* This could be avoided by storing the unmarshalled object (instead of a String) in the map.
202-
*/
203208
final GenericKubernetesResource resource = Serialization.unmarshal(jsonStringOfResource, GenericKubernetesResource.class);
204209
if (resource.getFinalizers().isEmpty()) {
205210
// No finalizers left, actually remove the resource.
206-
processEvent(path, pathAttributes, oldAttributes, null);
211+
processEvent(path, pathAttributes, oldAttributes, null, null);
207212
return;
208213
} else if (!resource.isMarkedForDeletion()) {
209214
// Mark the resource as deleted, but don't remove it yet (wait for finalizer-removal).
210215
resource.getMetadata().setDeletionTimestamp(LocalDateTime.now().toString());
211216
String updatedResource = Serialization.asJson(resource);
212-
processEvent(path, pathAttributes, oldAttributes, updatedResource);
217+
processEvent(path, pathAttributes, oldAttributes, resource, updatedResource);
213218
}
214219
// else: if the resource is already marked for deletion and still has finalizers, do nothing.
215220
}
@@ -226,11 +231,9 @@ public AttributeSet getKey(String path) {
226231

227232
@Override
228233
public Map.Entry<AttributeSet, String> findResource(AttributeSet attributes) {
229-
synchronized (map) {
230-
return map.entrySet().stream()
231-
.filter(entry -> entry.getKey().matches(attributes))
232-
.findFirst().orElse(null);
233-
}
234+
return map.entrySet().stream()
235+
.filter(entry -> entry.getKey().matches(attributes))
236+
.findFirst().orElse(null);
234237
}
235238

236239
@Override
@@ -239,11 +242,16 @@ public boolean isStatusSubresourceEnabledForResource(String path) {
239242
}
240243

241244
@Override
242-
public void processEvent(String path, AttributeSet pathAttributes, AttributeSet oldAttributes, String newState) {
245+
public void processEvent(String path, AttributeSet pathAttributes, AttributeSet oldAttributes,
246+
GenericKubernetesResource resource, String newState) {
243247
String existing = map.remove(oldAttributes);
244248
AttributeSet newAttributes = null;
245249
if (newState != null) {
246-
newAttributes = kubernetesAttributesExtractor.fromResource(newState);
250+
if (resource != null) {
251+
newAttributes = kubernetesAttributesExtractor.extract(resource);
252+
} else {
253+
newAttributes = kubernetesAttributesExtractor.fromResource(newState);
254+
}
247255
// corner case - we need to get the plural from the path
248256
if (!newAttributes.containsKey(KubernetesAttributesExtractor.PLURAL)) {
249257
newAttributes = AttributeSet.merge(pathAttributes, newAttributes);
@@ -283,11 +291,9 @@ public MockResponse handleWatch(String path) {
283291
}
284292
WatchEventsListener watchEventListener = new WatchEventsListener(context, query, watchEventListeners, LOGGER,
285293
watch -> {
286-
synchronized (map) {
287-
map.entrySet().stream()
288-
.filter(entry -> watch.attributeMatches(entry.getKey()))
289-
.forEach(entry -> watch.sendWebSocketResponse(entry.getValue(), Action.ADDED));
290-
}
294+
map.entrySet().stream()
295+
.filter(entry -> watch.attributeMatches(entry.getKey()))
296+
.forEach(entry -> watch.sendWebSocketResponse(entry.getValue(), Action.ADDED));
291297
});
292298
watchEventListeners.add(watchEventListener);
293299
mockResponse.setSocketPolicy(SocketPolicy.KEEP_OPEN);

junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/crud/KubernetesCrudPersistence.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.fasterxml.jackson.databind.JsonNode;
2020
import com.fasterxml.jackson.databind.ObjectReader;
2121
import com.fasterxml.jackson.databind.node.ObjectNode;
22+
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
2223
import io.fabric8.kubernetes.client.server.mock.Resetable;
2324
import io.fabric8.kubernetes.client.utils.Serialization;
2425
import io.fabric8.mockwebserver.crud.AttributeSet;
@@ -47,7 +48,8 @@ public interface KubernetesCrudPersistence extends Resetable {
4748

4849
boolean isStatusSubresourceEnabledForResource(String path);
4950

50-
void processEvent(String path, AttributeSet pathAttributes, AttributeSet oldAttributes, String newState);
51+
void processEvent(String path, AttributeSet pathAttributes, AttributeSet oldAttributes, GenericKubernetesResource resource,
52+
String newState);
5153

5254
default JsonNode asNode(Map.Entry<AttributeSet, String> resource) throws KubernetesCrudDispatcherException {
5355
return asNode(resource.getValue());

junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/crud/PatchHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,10 @@ public MockResponse handle(String path, String contentType, String requestBody)
8989
if (deserializedResource.isMarkedForDeletion() && deserializedResource.getFinalizers().isEmpty()) {
9090
// Delete the resource.
9191
updatedAsString = null;
92+
deserializedResource = null;
9293
}
9394

94-
persistence.processEvent(path, query, currentResourceEntry.getKey(), updatedAsString);
95+
persistence.processEvent(path, query, currentResourceEntry.getKey(), deserializedResource, updatedAsString);
9596
return new MockResponse().setResponseCode(HTTP_ACCEPTED).setBody(Utils.getNonNullOrElse(updatedAsString, ""));
9697
}
9798

junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/crud/PostHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public MockResponse handle(String path, String contentType, String requestBody)
6262
resource.getAdditionalProperties().remove(STATUS);
6363
}
6464
final String response = Serialization.asJson(resource);
65-
persistence.processEvent(path, attributes, null, response);
65+
persistence.processEvent(path, attributes, null, resource, response);
6666
return new MockResponse().setResponseCode(HttpURLConnection.HTTP_CREATED).setBody(response);
6767
}
6868

junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/crud/PutHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public MockResponse handle(String path, String contentType, String requestBody)
4747

4848
// Delete the resource if it is marked for deletion and has no finalizers.
4949
if (resource.isMarkedForDeletion() && resource.getFinalizers().isEmpty()) {
50-
persistence.processEvent(path, attributes, currentResourceEntry.getKey(), null);
50+
persistence.processEvent(path, attributes, currentResourceEntry.getKey(), null, null);
5151
return new MockResponse().setResponseCode(HttpURLConnection.HTTP_OK);
5252
}
5353

@@ -72,7 +72,7 @@ public MockResponse handle(String path, String contentType, String requestBody)
7272
}
7373
persistence.touchResourceVersion(currentResource, updatedResource);
7474
final String response = Serialization.asJson(updatedResource);
75-
persistence.processEvent(path, attributes, currentResourceEntry.getKey(), response);
75+
persistence.processEvent(path, attributes, currentResourceEntry.getKey(), null, response);
7676
return new MockResponse().setResponseCode(HttpURLConnection.HTTP_OK).setBody(response);
7777
}
7878
}

0 commit comments

Comments
 (0)