Skip to content

Commit 5bcf1ce

Browse files
committed
Remove JDK watch only after a delay, just to avoid hammering the registry api
1 parent 85efd25 commit 5bcf1ce

File tree

1 file changed

+30
-7
lines changed

1 file changed

+30
-7
lines changed

src/main/java/engineering/swat/watch/impl/BundledSubscription.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
import java.io.Closeable;
44
import java.io.IOException;
55
import java.util.List;
6+
import java.util.concurrent.CompletableFuture;
67
import java.util.concurrent.ConcurrentHashMap;
78
import java.util.concurrent.ConcurrentMap;
89
import java.util.concurrent.CopyOnWriteArrayList;
10+
import java.util.concurrent.TimeUnit;
911
import java.util.function.Consumer;
1012

13+
import org.apache.logging.log4j.LogManager;
14+
import org.apache.logging.log4j.Logger;
1115
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
1216
import org.checkerframework.checker.nullness.qual.NonNull;
1317

@@ -16,6 +20,7 @@
1620
* This is used (for example) to avoid multiple JDKPoller registries for the same path
1721
*/
1822
public class BundledSubscription<Key extends @NonNull Object, Event extends @NonNull Object> implements ISubscribable<Key,Event> {
23+
private static final Logger logger = LogManager.getLogger();
1924
private final ISubscribable<Key, Event> wrapped;
2025
private final ConcurrentMap<Key, Subscription<Event>> subscriptions = new ConcurrentHashMap<>();
2126

@@ -71,15 +76,33 @@ public Closeable subscribe(Key target, Consumer<Event> eventListener) throws IOE
7176
}
7277
}
7378
return () -> {
79+
boolean scheduleClose = false;
7480
synchronized(active) {
7581
active.remove(eventListener);
76-
if (!active.hasActiveConsumers() && !active.closed) {
77-
active.closed = true;
78-
this.subscriptions.remove(target, active);
79-
if (active.toBeClosed != null) {
80-
active.toBeClosed.close();
81-
}
82-
}
82+
scheduleClose = !active.hasActiveConsumers() && !active.closed;
83+
}
84+
if (scheduleClose) {
85+
// to avoid hammering the system with closes & registers in a short periode
86+
// we schedule the cleanup of watches in the background, when even after a small delay
87+
// nobody is interested in a certain file anymore
88+
CompletableFuture
89+
.delayedExecutor(100, TimeUnit.MILLISECONDS)
90+
.execute(() -> {
91+
synchronized(active) {
92+
if (!active.hasActiveConsumers() && !active.closed) {
93+
// still ready to be closed
94+
active.closed = true;
95+
this.subscriptions.remove(target, active);
96+
if (active.toBeClosed != null) {
97+
try {
98+
active.toBeClosed.close();
99+
} catch (IOException e) {
100+
logger.error("Unhandled exception while closing the watcher for {} in the background", target, e);
101+
}
102+
}
103+
}
104+
}
105+
});
83106
}
84107
};
85108
}

0 commit comments

Comments
 (0)