java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java
... ...
@@ -92,6 +92,14 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
92 92
*/
93 93
private final Map<DeviceIdentifier, Pair<TimeRange, List<DeviceMappingWithRegattaLogEvent<ItemT>>>> cachedMappings = new HashMap<>();
94 94
95
+ /**
96
+ * When the {@link #cachedMappings} are to be updated, the corresponding update job is stored in this field. It must be executed
97
+ * under the {@link #mappingsLock}'s write lock. However, when other updates to {@link #mappings} or {@link #mappingsByDevice}
98
+ * are performed (usually by the {@link #updateMappingsInternal()} method), this field will be cleared because the cache update
99
+ * will most likely be obsolete.
100
+ */
101
+ private Runnable cacheUpdateJob;
102
+
95 103
private int cacheHits;
96 104
private int cacheMisses;
97 105
... ...
@@ -182,7 +190,13 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
182 190
183 191
/**
184 192
* Calls the given callback for every DeviceMapping that is known for the given {@link DeviceIdentifier} that
185
- * includes the given {@link TimePoint}.
193
+ * includes the given {@link TimePoint}.<p>
194
+ *
195
+ * Searches the {@link #cachedMappings} for a match for the {@code device}; if found, checks whether {@code timePoint}
196
+ * is within the time range for which device mappings were cached, and if so, uses those device mappings. Otherwise,
197
+ * the device mappings are calculated and a cache update is carried out after releasing the read-lock of
198
+ * {@link #mappingsLock} and after obtaining its write-lock. Should an update have been squeezed in between releasing
199
+ * the read-lock and obtaining the write-lock, the cache update is not carried out.
186 200
*
187 201
* @param device
188 202
* the device to get the mappings for
... ...
@@ -204,15 +218,14 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
204 218
cachedTimeRangeForDevice.getB().forEach(mapping->callback.accept(mapping));
205 219
} else {
206 220
final List<DeviceMappingWithRegattaLogEvent<ItemT>> mappingsForDevice = mappingsByDevice.get(device);
207
- MultiTimeRange timeRangeForCache = MultiTimeRange.of();
221
+ TimeRange timeRangeForCache = null;
208 222
final List<DeviceMappingWithRegattaLogEvent<ItemT>> deviceMappingsForCache = new LinkedList<>();
209
- assert timeRangeForCache.isEmpty();
210 223
cacheMisses++;
211 224
if (mappingsForDevice != null) {
212 225
for (final DeviceMappingWithRegattaLogEvent<ItemT> mapping : mappingsForDevice) {
213 226
if (mapping.getTimeRange().includes(timePoint)) {
214
- if (timeRangeForCache.isEmpty()) {
215
- timeRangeForCache.union(mapping.getTimeRange());
227
+ if (timeRangeForCache == null) {
228
+ timeRangeForCache = mapping.getTimeRange();
216 229
} else {
217 230
timeRangeForCache = timeRangeForCache.intersection(mapping.getTimeRange());
218 231
}
... ...
@@ -221,12 +234,20 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
221 234
}
222 235
}
223 236
}
224
- final MultiTimeRange finalTimeRangeForache = timeRangeForCache;
237
+ final TimeRange finalTimeRangeForCache = timeRangeForCache;
225 238
logger.fine(() -> "Device mapping cache miss for mapper " + this + " for device " + device
226 239
+ " and time point " + timePoint + ", determined cachable range "
227
- + finalTimeRangeForache + "; " + cacheHits + " hits, " + cacheMisses + " misses");
228
-
229
- // TODO bug6236 Now issue the caching of the entry (device, (timeRangeForCache, deviceMappingsForCache)) under the mappingsLock's write lock!
240
+ + finalTimeRangeForCache + "; " + cacheHits + " hits, " + cacheMisses + " misses");
241
+ cacheUpdateJob = ()->cachedMappings.put(device, new Pair<>(finalTimeRangeForCache, deviceMappingsForCache));
242
+ }
243
+ });
244
+ LockUtil.executeWithWriteLock(mappingsLock, ()->{
245
+ if (cacheUpdateJob != null) {
246
+ logger.fine(()-> "Device mapping cache miss for mapper " + this + " performs cache update.");
247
+ cacheUpdateJob.run();
248
+ } else {
249
+ logger.fine(() -> "Device mapping cache miss for mapper " + this
250
+ + " does not update the cache because the mappings were updated in between");
230 251
}
231 252
});
232 253
}
... ...
@@ -299,6 +320,7 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
299 320
cachedMappings.clear();
300 321
mappings.putAll(newMappings);
301 322
mappingsByDevice.clear();
323
+ cacheUpdateJob = null;
302 324
for (ItemT item : newMappings.keySet()) {
303 325
for (DeviceMappingWithRegattaLogEvent<ItemT> mapping : newMappings.get(item)) {
304 326
List<DeviceMappingWithRegattaLogEvent<ItemT>> list = mappingsByDevice.get(mapping.getDevice());
java/target/configuration/logging_debug.properties
... ...
@@ -68,4 +68,7 @@ com.sap.sse.landscape.impl.GithubReleasesRepository.level = FINE
68 68
69 69
# Produce wind-from-maneuver estimation graph:
70 70
#com.sap.sailing.windestimation.integration.IncrementalMstHmmWindEstimationForTrackedRaceTest.level = FINE
71
-#com.sap.sailing.windestimation.integration.IncrementalMstHmmWindEstimationForTrackedRace.level = FINE
... ...
\ No newline at end of file
0
+#com.sap.sailing.windestimation.integration.IncrementalMstHmmWindEstimationForTrackedRace.level = FINE
1
+
2
+# Log device mapping caching
3
+com.sap.sailing.domain.racelogtracking.impl.fixtracker.RegattaLogDeviceMappings.level = FINE