aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/traccar/database/DeviceLookupService.java83
1 files changed, 77 insertions, 6 deletions
diff --git a/src/main/java/org/traccar/database/DeviceLookupService.java b/src/main/java/org/traccar/database/DeviceLookupService.java
index f0a4e7bf0..9cf0899ee 100644
--- a/src/main/java/org/traccar/database/DeviceLookupService.java
+++ b/src/main/java/org/traccar/database/DeviceLookupService.java
@@ -15,40 +15,111 @@
*/
package org.traccar.database;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.model.Device;
import org.traccar.storage.Storage;
+import org.traccar.storage.StorageException;
import org.traccar.storage.query.Columns;
import org.traccar.storage.query.Condition;
import org.traccar.storage.query.Request;
import javax.inject.Inject;
import javax.inject.Singleton;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
@Singleton
public class DeviceLookupService {
private static final Logger LOGGER = LoggerFactory.getLogger(DeviceLookupService.class);
+ private static final long INFO_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(60);
+ private static final long THROTTLE_MIN_MS = TimeUnit.MINUTES.toMillis(1);
+ private static final long THROTTLE_MAX_MS = TimeUnit.MINUTES.toMillis(30);
+
private final Storage storage;
+ private final Timer timer;
+
+ private static class IdentifierInfo {
+ private long lastQuery;
+ private long delay;
+ private Timeout timeout;
+ }
+
+ private final class IdentifierTask implements TimerTask {
+ private final String uniqueId;
+
+ private IdentifierTask(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public void run(Timeout timeout) {
+ LOGGER.debug("Device lookup expired {}", uniqueId);
+ synchronized (DeviceLookupService.this) {
+ identifierMap.remove(uniqueId);
+ }
+ }
+ }
+
+ private final Map<String, IdentifierInfo> identifierMap = new ConcurrentHashMap<>();
@Inject
- public DeviceLookupService(Storage storage) {
+ public DeviceLookupService(Storage storage, Timer timer) {
this.storage = storage;
+ this.timer = timer;
+ }
+
+ private synchronized boolean isThrottled(String uniqueId) {
+ IdentifierInfo info = identifierMap.get(uniqueId);
+ return info != null && System.currentTimeMillis() < info.lastQuery + info.delay;
+ }
+
+ private synchronized void lookupSucceeded(String uniqueId) {
+ IdentifierInfo info = identifierMap.remove(uniqueId);
+ if (info != null) {
+ info.timeout.cancel();
+ }
+ }
+
+ private synchronized void lookupFailed(String uniqueId) {
+ IdentifierInfo info = identifierMap.get(uniqueId);
+ if (info != null) {
+ info.timeout.cancel();
+ info.delay = Math.min(info.delay * 2, THROTTLE_MAX_MS);
+ } else {
+ info = new IdentifierInfo();
+ identifierMap.put(uniqueId, info);
+ info.delay = THROTTLE_MIN_MS;
+ }
+ info.lastQuery = System.currentTimeMillis();
+ info.timeout = timer.newTimeout(new IdentifierTask(uniqueId), INFO_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ LOGGER.debug("Device lookup {} throttled for {} ms", uniqueId, info.delay);
}
public Device lookup(String[] uniqueIds) {
Device device = null;
try {
for (String uniqueId : uniqueIds) {
- device = storage.getObject(Device.class, new Request(
- new Columns.All(), new Condition.Equals("uniqueId", "uniqueId", uniqueId)));
- if (device != null) {
- break;
+ if (!isThrottled(uniqueId)) {
+ device = storage.getObject(Device.class, new Request(
+ new Columns.All(), new Condition.Equals("uniqueId", "uniqueId", uniqueId)));
+ if (device != null) {
+ lookupSucceeded(uniqueId);
+ break;
+ } else {
+ lookupFailed(uniqueId);
+ }
+ } else {
+ LOGGER.debug("Device lookup throttled {}", uniqueId);
}
}
- } catch (Exception e) {
+ } catch (StorageException e) {
LOGGER.warn("Find device error", e);
}
return device;