abb891e7876a84eb064cce1612faa66e87b9d7cb
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java
| ... | ... | @@ -210,158 +210,159 @@ public class FixLoaderAndTracker implements TrackingDataLoader { |
| 210 | 210 | Iterable<Timed> fixes, boolean returnManeuverChanges, boolean returnLiveDelay) { |
| 211 | 211 | final Set<RegattaAndRaceIdentifier> maneuverChanged = new HashSet<>(); |
| 212 | 212 | final Map<RegattaAndRaceIdentifier, Duration> delayToLive = new HashMap<>(); |
| 213 | - // TODO bug6236: how to improve performance of device mappings look-up when we have received multiple fixes from the same device? |
|
| 214 | 213 | if (!preemptiveStopRequested.get() && trackedRace.getStartOfTracking() != null) { |
| 215 | - final TimePoint timePoint = fix.getTimePoint(); |
|
| 216 | - deviceMappings.forEachMappingOfDeviceIncludingTimePoint(device, fix.getTimePoint(), |
|
| 217 | - new Consumer<DeviceMappingWithRegattaLogEvent<WithID>>() { |
|
| 218 | - @Override |
|
| 219 | - public void accept(DeviceMappingWithRegattaLogEvent<WithID> mapping) { |
|
| 220 | - mapping.getRegattaLogEvent().accept(new MappingEventVisitor() { |
|
| 221 | - @Override |
|
| 222 | - public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) { |
|
| 223 | - recordSensorFixForCompetitor(event.getMappedTo(), event); |
|
| 224 | - } |
|
| 225 | - |
|
| 226 | - @Override |
|
| 227 | - public void visit(RegattaLogDeviceBoatSensorDataMappingEvent event) { |
|
| 228 | - final Boat boat = event.getMappedTo(); |
|
| 229 | - final Competitor competitor = trackedRace.getCompetitorOfBoat(boat); |
|
| 230 | - if (competitor != null) { |
|
| 231 | - recordSensorFixForCompetitor(competitor, event); |
|
| 232 | - } else { |
|
| 233 | - logger.log(Level.FINE, ()->"Could not record fix for boat because no competitor could be determined. Boat: " + boat); |
|
| 214 | + for (final Timed fix : fixes) { |
|
| 215 | + final TimePoint timePoint = fix.getTimePoint(); |
|
| 216 | + deviceMappings.forEachMappingOfDeviceIncludingTimePoint(device, fix.getTimePoint(), |
|
| 217 | + new Consumer<DeviceMappingWithRegattaLogEvent<WithID>>() { |
|
| 218 | + @Override |
|
| 219 | + public void accept(DeviceMappingWithRegattaLogEvent<WithID> mapping) { |
|
| 220 | + mapping.getRegattaLogEvent().accept(new MappingEventVisitor() { |
|
| 221 | + @Override |
|
| 222 | + public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) { |
|
| 223 | + recordSensorFixForCompetitor(event.getMappedTo(), event); |
|
| 234 | 224 | } |
| 235 | - } |
|
| 236 | - |
|
| 237 | - private void recordSensorFixForCompetitor(Competitor competitor, RegattaLogDeviceMappingEvent<?> event) { |
|
| 238 | - if (!preemptiveStopRequested.get()) { |
|
| 239 | - @SuppressWarnings("unchecked") |
|
| 240 | - SensorFixMapper<SensorFix, DynamicSensorFixTrack<Competitor, SensorFix>, Competitor> mapper = sensorFixMapperFactory |
|
| 241 | - .createCompetitorMapper((Class<? extends RegattaLogDeviceMappingEvent<?>>) event.getClass()); |
|
| 242 | - DynamicSensorFixTrack<Competitor, SensorFix> track = mapper.getTrack(trackedRace, competitor); |
|
| 243 | - if (track != null && trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) { |
|
| 244 | - mapper.addFix(track, (DoubleVectorFix) fix); |
|
| 245 | - if (returnLiveDelay) { |
|
| 246 | - delayToLive.put(trackedRace.getRaceIdentifier(), new MillisecondsDurationImpl(trackedRace.getDelayToLiveInMillis())); |
|
| 247 | - } |
|
| 225 | + |
|
| 226 | + @Override |
|
| 227 | + public void visit(RegattaLogDeviceBoatSensorDataMappingEvent event) { |
|
| 228 | + final Boat boat = event.getMappedTo(); |
|
| 229 | + final Competitor competitor = trackedRace.getCompetitorOfBoat(boat); |
|
| 230 | + if (competitor != null) { |
|
| 231 | + recordSensorFixForCompetitor(competitor, event); |
|
| 232 | + } else { |
|
| 233 | + logger.log(Level.FINE, ()->"Could not record fix for boat because no competitor could be determined. Boat: " + boat); |
|
| 248 | 234 | } |
| 249 | 235 | } |
| 250 | - } |
|
| 251 | - |
|
| 252 | - @Override |
|
| 253 | - public void visit(RegattaLogDeviceCompetitorMappingEvent event) { |
|
| 254 | - recordForCompetitor(event.getMappedTo()); |
|
| 255 | - } |
|
| 256 | - |
|
| 257 | - @Override |
|
| 258 | - public void visit(RegattaLogDeviceBoatMappingEvent event) { |
|
| 259 | - final Boat boat = event.getMappedTo(); |
|
| 260 | - final Competitor comp = trackedRace.getCompetitorOfBoat(boat); |
|
| 261 | - if (comp != null) { |
|
| 262 | - recordForCompetitor(comp); |
|
| 263 | - } else { |
|
| 264 | - // this is not necessarily something to warn of; while a boat tracker may continuously track |
|
| 265 | - logger.log(Level.FINE, |
|
| 266 | - ()->"Could not record fix for boat because no competitor could be determined. Boat: " + boat); |
|
| 267 | - } |
|
| 268 | - } |
|
| 269 | - |
|
| 270 | - private void recordForCompetitor(Competitor comp) { |
|
| 271 | - if (!preemptiveStopRequested.get()) { |
|
| 272 | - if (fix instanceof GPSFixMoving) { |
|
| 273 | - // try to record the fix, and only if it was really to the track, |
|
| 274 | - // check for maneuvers; otherwise, the fix may not have been accepted |
|
| 275 | - // by the race or the track, e.g., because the race's end-of-tracking |
|
| 276 | - // comes before the fix's time point |
|
| 277 | - if (trackedRace.recordFix(comp, (GPSFixMoving) fix)) { // TOOD bug6229: this checks the TrackedRace's tracking interval, but for an MDI we'd also want to intersect with Event/Regatta end date if set |
|
| 278 | - if (returnManeuverChanges) { |
|
| 279 | - RegattaAndRaceIdentifier maneuverChangedAnswer = detectIfManeuverChanged(comp); |
|
| 280 | - if (maneuverChangedAnswer != null) { |
|
| 281 | - maneuverChanged.add(maneuverChangedAnswer); |
|
| 282 | - } |
|
| 283 | - } |
|
| 236 | + |
|
| 237 | + private void recordSensorFixForCompetitor(Competitor competitor, RegattaLogDeviceMappingEvent<?> event) { |
|
| 238 | + if (!preemptiveStopRequested.get()) { |
|
| 239 | + @SuppressWarnings("unchecked") |
|
| 240 | + SensorFixMapper<SensorFix, DynamicSensorFixTrack<Competitor, SensorFix>, Competitor> mapper = sensorFixMapperFactory |
|
| 241 | + .createCompetitorMapper((Class<? extends RegattaLogDeviceMappingEvent<?>>) event.getClass()); |
|
| 242 | + DynamicSensorFixTrack<Competitor, SensorFix> track = mapper.getTrack(trackedRace, competitor); |
|
| 243 | + if (track != null && trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) { |
|
| 244 | + mapper.addFix(track, (DoubleVectorFix) fix); |
|
| 284 | 245 | if (returnLiveDelay) { |
| 285 | 246 | delayToLive.put(trackedRace.getRaceIdentifier(), new MillisecondsDurationImpl(trackedRace.getDelayToLiveInMillis())); |
| 286 | 247 | } |
| 287 | 248 | } |
| 288 | - } else { |
|
| 289 | - logger.log(Level.WARNING, |
|
| 290 | - String.format( |
|
| 291 | - "Could not add fix for competitor (%s) in race (%s), as it" |
|
| 292 | - + " is no GPSFixMoving, meaning it is missing COG/SOG values", |
|
| 293 | - comp, trackedRace.getRace().getName())); |
|
| 294 | 249 | } |
| 295 | 250 | } |
| 296 | - } |
|
| 297 | - |
|
| 298 | - @Override |
|
| 299 | - public void visit(RegattaLogDeviceMarkMappingEvent event) { |
|
| 300 | - if (!preemptiveStopRequested.get()) { |
|
| 301 | - Mark mark = event.getMappedTo(); |
|
| 302 | - final DynamicGPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getOrCreateTrack(mark); |
|
| 303 | - final GPSFix firstFixAtOrAfter; |
|
| 304 | - final boolean forceFix; |
|
| 305 | - if (trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) { |
|
| 306 | - forceFix = false; |
|
| 251 | + |
|
| 252 | + @Override |
|
| 253 | + public void visit(RegattaLogDeviceCompetitorMappingEvent event) { |
|
| 254 | + recordForCompetitor(event.getMappedTo()); |
|
| 255 | + } |
|
| 256 | + |
|
| 257 | + @Override |
|
| 258 | + public void visit(RegattaLogDeviceBoatMappingEvent event) { |
|
| 259 | + final Boat boat = event.getMappedTo(); |
|
| 260 | + final Competitor comp = trackedRace.getCompetitorOfBoat(boat); |
|
| 261 | + if (comp != null) { |
|
| 262 | + recordForCompetitor(comp); |
|
| 307 | 263 | } else { |
| 308 | - markTrack.lockForRead(); |
|
| 309 | - try { |
|
| 310 | - if (Util.isEmpty(markTrack.getRawFixes()) |
|
| 311 | - || (firstFixAtOrAfter = markTrack.getFirstFixAtOrAfter(timePoint)) != null |
|
| 312 | - && firstFixAtOrAfter.getTimePoint().equals(timePoint)) { |
|
| 313 | - // either the first fix or overwriting an existing one |
|
| 314 | - forceFix = true; |
|
| 315 | - } else { |
|
| 316 | - // checking if the given fix is "better" than an existing one |
|
| 317 | - TimePoint startOfTracking = trackedRace.getStartOfTracking(); |
|
| 318 | - TimePoint endOfTracking = trackedRace.getEndOfTracking(); |
|
| 319 | - if (startOfTracking != null) { |
|
| 320 | - GPSFix fixAfterStartOfTracking = markTrack |
|
| 321 | - .getFirstFixAtOrAfter(startOfTracking); |
|
| 322 | - if (fixAfterStartOfTracking == null |
|
| 323 | - || !trackedRace.isWithinStartAndEndOfTracking( |
|
| 324 | - fixAfterStartOfTracking.getTimePoint())) { |
|
| 325 | - // There is no fix in the tracking interval, so this fix could be "better" |
|
| 326 | - // than ones already available in the track |
|
| 327 | - // Better means closer before/after the beginning/end of the tracking |
|
| 328 | - // interval |
|
| 329 | - if (timePoint.before(startOfTracking)) { |
|
| 330 | - // check if it is closer to the beginning of the tracking interval |
|
| 331 | - GPSFix fixBeforeStartOfTracking = markTrack |
|
| 332 | - .getLastFixAtOrBefore(startOfTracking); |
|
| 333 | - forceFix = (fixBeforeStartOfTracking == null |
|
| 334 | - || fixBeforeStartOfTracking.getTimePoint().before(timePoint)); |
|
| 335 | - } else if (endOfTracking != null && timePoint.after(endOfTracking)) { |
|
| 336 | - // check if it is closer to the end of the tracking interval |
|
| 337 | - GPSFix fixAfterEndOfTracking = markTrack |
|
| 338 | - .getFirstFixAtOrAfter(endOfTracking); |
|
| 339 | - forceFix = (fixAfterEndOfTracking == null |
|
| 340 | - || fixAfterEndOfTracking.getTimePoint().after(timePoint)); |
|
| 264 | + // this is not necessarily something to warn of; while a boat tracker may continuously track |
|
| 265 | + logger.log(Level.FINE, |
|
| 266 | + ()->"Could not record fix for boat because no competitor could be determined. Boat: " + boat); |
|
| 267 | + } |
|
| 268 | + } |
|
| 269 | + |
|
| 270 | + private void recordForCompetitor(Competitor comp) { |
|
| 271 | + if (!preemptiveStopRequested.get()) { |
|
| 272 | + if (fix instanceof GPSFixMoving) { |
|
| 273 | + // try to record the fix, and only if it was really to the track, |
|
| 274 | + // check for maneuvers; otherwise, the fix may not have been accepted |
|
| 275 | + // by the race or the track, e.g., because the race's end-of-tracking |
|
| 276 | + // comes before the fix's time point |
|
| 277 | + if (trackedRace.recordFix(comp, (GPSFixMoving) fix)) { // TOOD bug6229: this checks the TrackedRace's tracking interval, but for an MDI we'd also want to intersect with Event/Regatta end date if set |
|
| 278 | + if (returnManeuverChanges) { |
|
| 279 | + RegattaAndRaceIdentifier maneuverChangedAnswer = detectIfManeuverChanged(comp); |
|
| 280 | + if (maneuverChangedAnswer != null) { |
|
| 281 | + maneuverChanged.add(maneuverChangedAnswer); |
|
| 282 | + } |
|
| 283 | + } |
|
| 284 | + if (returnLiveDelay) { |
|
| 285 | + delayToLive.put(trackedRace.getRaceIdentifier(), new MillisecondsDurationImpl(trackedRace.getDelayToLiveInMillis())); |
|
| 286 | + } |
|
| 287 | + } |
|
| 288 | + } else { |
|
| 289 | + logger.log(Level.WARNING, |
|
| 290 | + String.format( |
|
| 291 | + "Could not add fix for competitor (%s) in race (%s), as it" |
|
| 292 | + + " is no GPSFixMoving, meaning it is missing COG/SOG values", |
|
| 293 | + comp, trackedRace.getRace().getName())); |
|
| 294 | + } |
|
| 295 | + } |
|
| 296 | + } |
|
| 297 | + |
|
| 298 | + @Override |
|
| 299 | + public void visit(RegattaLogDeviceMarkMappingEvent event) { |
|
| 300 | + if (!preemptiveStopRequested.get()) { |
|
| 301 | + Mark mark = event.getMappedTo(); |
|
| 302 | + final DynamicGPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getOrCreateTrack(mark); |
|
| 303 | + final GPSFix firstFixAtOrAfter; |
|
| 304 | + final boolean forceFix; |
|
| 305 | + if (trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) { |
|
| 306 | + forceFix = false; |
|
| 307 | + } else { |
|
| 308 | + markTrack.lockForRead(); |
|
| 309 | + try { |
|
| 310 | + if (Util.isEmpty(markTrack.getRawFixes()) |
|
| 311 | + || (firstFixAtOrAfter = markTrack.getFirstFixAtOrAfter(timePoint)) != null |
|
| 312 | + && firstFixAtOrAfter.getTimePoint().equals(timePoint)) { |
|
| 313 | + // either the first fix or overwriting an existing one |
|
| 314 | + forceFix = true; |
|
| 315 | + } else { |
|
| 316 | + // checking if the given fix is "better" than an existing one |
|
| 317 | + TimePoint startOfTracking = trackedRace.getStartOfTracking(); |
|
| 318 | + TimePoint endOfTracking = trackedRace.getEndOfTracking(); |
|
| 319 | + if (startOfTracking != null) { |
|
| 320 | + GPSFix fixAfterStartOfTracking = markTrack |
|
| 321 | + .getFirstFixAtOrAfter(startOfTracking); |
|
| 322 | + if (fixAfterStartOfTracking == null |
|
| 323 | + || !trackedRace.isWithinStartAndEndOfTracking( |
|
| 324 | + fixAfterStartOfTracking.getTimePoint())) { |
|
| 325 | + // There is no fix in the tracking interval, so this fix could be "better" |
|
| 326 | + // than ones already available in the track |
|
| 327 | + // Better means closer before/after the beginning/end of the tracking |
|
| 328 | + // interval |
|
| 329 | + if (timePoint.before(startOfTracking)) { |
|
| 330 | + // check if it is closer to the beginning of the tracking interval |
|
| 331 | + GPSFix fixBeforeStartOfTracking = markTrack |
|
| 332 | + .getLastFixAtOrBefore(startOfTracking); |
|
| 333 | + forceFix = (fixBeforeStartOfTracking == null |
|
| 334 | + || fixBeforeStartOfTracking.getTimePoint().before(timePoint)); |
|
| 335 | + } else if (endOfTracking != null && timePoint.after(endOfTracking)) { |
|
| 336 | + // check if it is closer to the end of the tracking interval |
|
| 337 | + GPSFix fixAfterEndOfTracking = markTrack |
|
| 338 | + .getFirstFixAtOrAfter(endOfTracking); |
|
| 339 | + forceFix = (fixAfterEndOfTracking == null |
|
| 340 | + || fixAfterEndOfTracking.getTimePoint().after(timePoint)); |
|
| 341 | + } else { |
|
| 342 | + forceFix = false; |
|
| 343 | + } |
|
| 341 | 344 | } else { |
| 345 | + // there is already a fix in the tracking interval |
|
| 342 | 346 | forceFix = false; |
| 343 | 347 | } |
| 344 | 348 | } else { |
| 345 | - // there is already a fix in the tracking interval |
|
| 346 | 349 | forceFix = false; |
| 347 | 350 | } |
| 348 | - } else { |
|
| 349 | - forceFix = false; |
|
| 350 | 351 | } |
| 352 | + } finally { |
|
| 353 | + markTrack.unlockAfterRead(); |
|
| 351 | 354 | } |
| 352 | - } finally { |
|
| 353 | - markTrack.unlockAfterRead(); |
|
| 354 | 355 | } |
| 355 | - } |
|
| 356 | - trackedRace.recordFix(mark, (GPSFix) fix, /* only when in tracking interval */ !forceFix); |
|
| 357 | - if (returnLiveDelay) { |
|
| 358 | - delayToLive.put(trackedRace.getRaceIdentifier(), new MillisecondsDurationImpl(trackedRace.getDelayToLiveInMillis())); |
|
| 356 | + trackedRace.recordFix(mark, (GPSFix) fix, /* only when in tracking interval */ !forceFix); |
|
| 357 | + if (returnLiveDelay) { |
|
| 358 | + delayToLive.put(trackedRace.getRaceIdentifier(), new MillisecondsDurationImpl(trackedRace.getDelayToLiveInMillis())); |
|
| 359 | + } |
|
| 359 | 360 | } |
| 360 | 361 | } |
| 361 | - } |
|
| 362 | - }); |
|
| 363 | - } |
|
| 364 | - }); |
|
| 362 | + }); |
|
| 363 | + } |
|
| 364 | + }); |
|
| 365 | + } |
|
| 365 | 366 | } |
| 366 | 367 | return mergeManeuverChangedAndLiveDelayResult(maneuverChanged, delayToLive); |
| 367 | 368 | } |
| ... | ... | @@ -879,6 +880,11 @@ public class FixLoaderAndTracker implements TrackingDataLoader { |
| 879 | 880 | addLoadingJob(new LoadFixesForNewlyCoveredTimeRangesJob(item, newlyCoveredTimeRanges)); |
| 880 | 881 | } |
| 881 | 882 | } |
| 883 | + |
|
| 884 | + @Override |
|
| 885 | + public String toString() { |
|
| 886 | + return "FixLoaderDeviceMappings for race "+trackedRace.getRaceIdentifier(); |
|
| 887 | + } |
|
| 882 | 888 | } |
| 883 | 889 | |
| 884 | 890 | /** |
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java
| ... | ... | @@ -4,6 +4,7 @@ import java.util.ArrayList; |
| 4 | 4 | import java.util.Collections; |
| 5 | 5 | import java.util.HashMap; |
| 6 | 6 | import java.util.HashSet; |
| 7 | +import java.util.LinkedList; |
|
| 7 | 8 | import java.util.List; |
| 8 | 9 | import java.util.Map; |
| 9 | 10 | import java.util.Set; |
| ... | ... | @@ -69,6 +70,31 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> { |
| 69 | 70 | private final Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> mappings = new HashMap<>(); |
| 70 | 71 | private final Map<DeviceIdentifier, List<DeviceMappingWithRegattaLogEvent<ItemT>>> mappingsByDevice = new HashMap<>(); |
| 71 | 72 | |
| 73 | + /** |
|
| 74 | + * A cache that holds the device mappings as the {@link Pair#getB() second} component of the values in this map, |
|
| 75 | + * such that exactly these device mappings apply for any time point {@link TimeRange#includes(TimePoint) included} |
|
| 76 | + * by the {@link TimeRange} that is the {@link Pair#getA() first} component of a value in this map. This map's keys |
|
| 77 | + * match with the {@link DeviceMapping#getDevice() device identifiers} of the {@link Pair#getB() second} components |
|
| 78 | + * of their corresponding values. |
|
| 79 | + * <p> |
|
| 80 | + * |
|
| 81 | + * This cache is designed to work well for cases where mappings change at a frequency orders of magnitude less than |
|
| 82 | + * the frequency with which fixes arrive and are to be mapped to items. Furthermore, the cache hit rates benefit |
|
| 83 | + * from mappings covering large time ranges. |
|
| 84 | + * <p> |
|
| 85 | + * |
|
| 86 | + * Any change to the mappings for a device will remove the mapping for the device's {@link DeviceIdentifier |
|
| 87 | + * identifier} from this map. |
|
| 88 | + * <p> |
|
| 89 | + * |
|
| 90 | + * Access to this map has to undergo the same locking drill as any access to {@link #mappings}, using the |
|
| 91 | + * {@link #mappingsLock}. |
|
| 92 | + */ |
|
| 93 | + private final Map<DeviceIdentifier, Pair<TimeRange, List<DeviceMappingWithRegattaLogEvent<ItemT>>>> cachedMappings = new HashMap<>(); |
|
| 94 | + |
|
| 95 | + private int cacheHits; |
|
| 96 | + private int cacheMisses; |
|
| 97 | + |
|
| 72 | 98 | private final RegattaLogEventVisitor regattaLogEventVisitor = new BaseRegattaLogEventVisitor() { |
| 73 | 99 | @Override |
| 74 | 100 | public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) { |
| ... | ... | @@ -169,13 +195,38 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> { |
| 169 | 195 | public void forEachMappingOfDeviceIncludingTimePoint(DeviceIdentifier device, TimePoint timePoint, |
| 170 | 196 | Consumer<DeviceMappingWithRegattaLogEvent<ItemT>> callback) { |
| 171 | 197 | LockUtil.executeWithReadLock(mappingsLock, () -> { |
| 172 | - List<DeviceMappingWithRegattaLogEvent<ItemT>> mappingsForDevice = mappingsByDevice.get(device); |
|
| 173 | - if (mappingsForDevice != null) { |
|
| 174 | - for (DeviceMappingWithRegattaLogEvent<ItemT> mapping : mappingsForDevice) { |
|
| 175 | - if (mapping.getTimeRange().includes(timePoint)) { |
|
| 176 | - callback.accept(mapping); |
|
| 198 | + final Pair<TimeRange, List<DeviceMappingWithRegattaLogEvent<ItemT>>> cachedTimeRangeForDevice = cachedMappings.get(device); |
|
| 199 | + if (cachedTimeRangeForDevice != null && cachedTimeRangeForDevice.getA().includes(timePoint)) { |
|
| 200 | + cacheHits++; |
|
| 201 | + logger.fine(() -> "Device mapping cache hit for mapper " + this + " for device " + device |
|
| 202 | + + " and time point " + timePoint + ", included in cached range " |
|
| 203 | + + cachedTimeRangeForDevice.getA() + "; " + cacheHits + " hits, " + cacheMisses + " misses"); |
|
| 204 | + cachedTimeRangeForDevice.getB().forEach(mapping->callback.accept(mapping)); |
|
| 205 | + } else { |
|
| 206 | + final List<DeviceMappingWithRegattaLogEvent<ItemT>> mappingsForDevice = mappingsByDevice.get(device); |
|
| 207 | + MultiTimeRange timeRangeForCache = MultiTimeRange.of(); |
|
| 208 | + final List<DeviceMappingWithRegattaLogEvent<ItemT>> deviceMappingsForCache = new LinkedList<>(); |
|
| 209 | + assert timeRangeForCache.isEmpty(); |
|
| 210 | + cacheMisses++; |
|
| 211 | + if (mappingsForDevice != null) { |
|
| 212 | + for (final DeviceMappingWithRegattaLogEvent<ItemT> mapping : mappingsForDevice) { |
|
| 213 | + if (mapping.getTimeRange().includes(timePoint)) { |
|
| 214 | + if (timeRangeForCache.isEmpty()) { |
|
| 215 | + timeRangeForCache.union(mapping.getTimeRange()); |
|
| 216 | + } else { |
|
| 217 | + timeRangeForCache = timeRangeForCache.intersection(mapping.getTimeRange()); |
|
| 218 | + } |
|
| 219 | + deviceMappingsForCache.add(mapping); |
|
| 220 | + callback.accept(mapping); |
|
| 221 | + } |
|
| 177 | 222 | } |
| 178 | 223 | } |
| 224 | + final MultiTimeRange finalTimeRangeForache = timeRangeForCache; |
|
| 225 | + logger.fine(() -> "Device mapping cache miss for mapper " + this + " for device " + device |
|
| 226 | + + " 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! |
|
| 179 | 230 | } |
| 180 | 231 | }); |
| 181 | 232 | } |
| ... | ... | @@ -245,6 +296,7 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> { |
| 245 | 296 | oldMappings.putAll(mappings); |
| 246 | 297 | oldDeviceIds.addAll(mappingsByDevice.keySet()); |
| 247 | 298 | mappings.clear(); |
| 299 | + cachedMappings.clear(); |
|
| 248 | 300 | mappings.putAll(newMappings); |
| 249 | 301 | mappingsByDevice.clear(); |
| 250 | 302 | for (ItemT item : newMappings.keySet()) { |