Refactored AsyncJSONResolverService to reduce duplicate code

This commit is contained in:
Risto Lahtela 2021-02-11 18:09:59 +02:00
parent 92e98948ec
commit 2c9c691b4f

View File

@ -61,118 +61,47 @@ public class AsyncJSONResolverService {
accessLock = new UnitSemaphoreAccessLock(); accessLock = new UnitSemaphoreAccessLock();
} }
public <T> JSONStorage.StoredJSON resolve(long newerThanTimestamp, DataID dataID, UUID serverUUID, Function<UUID, T> creator) { public <T> JSONStorage.StoredJSON resolve(
long newerThanTimestamp, DataID dataID, UUID serverUUID, Function<UUID, T> creator
) {
String identifier = dataID.of(serverUUID); String identifier = dataID.of(serverUUID);
Supplier<T> jsonCreator = () -> creator.apply(serverUUID);
// Attempt to find a newer version of the json file from cache return getStoredOrCreateJSON(newerThanTimestamp, identifier, jsonCreator);
JSONStorage.StoredJSON storedJSON = jsonStorage.fetchExactJson(identifier, newerThanTimestamp)
.orElseGet(() -> jsonStorage.fetchJsonMadeAfter(identifier, newerThanTimestamp)
.orElse(null));
if (storedJSON != null) {
return storedJSON;
}
// No new enough version, let's refresh and send old version of the file
long updateThreshold = config.get(WebserverSettings.REDUCED_REFRESH_BARRIER);
// Check if the json is already being created
Future<JSONStorage.StoredJSON> updatedJSON;
accessLock.enter();
try {
updatedJSON = currentlyProcessing.get(identifier);
if (updatedJSON == null && previousUpdates.getOrDefault(identifier, 0L) < newerThanTimestamp - updateThreshold) {
// Submit a task to refresh the data if the json is old
updatedJSON = processing.submitNonCritical(() -> {
JSONStorage.StoredJSON created = jsonStorage.storeJson(identifier, creator.apply(serverUUID));
currentlyProcessing.remove(identifier);
jsonStorage.invalidateOlder(identifier, created.timestamp);
previousUpdates.put(identifier, created.timestamp);
return created;
});
currentlyProcessing.put(identifier, updatedJSON);
}
} finally {
accessLock.exit();
} }
// Get an old version from cache
storedJSON = jsonStorage.fetchJsonMadeBefore(identifier, newerThanTimestamp).orElse(null);
if (storedJSON != null) {
return storedJSON;
} else {
// If there is no version available, block thread until the new finishes being generated.
try {
// Can be null if the last update was recent and the file is deleted before next update
if (updatedJSON == null) {
updatedJSON = processing.submitNonCritical(() -> {
JSONStorage.StoredJSON created = jsonStorage.storeJson(identifier, creator.apply(serverUUID));
currentlyProcessing.remove(identifier);
jsonStorage.invalidateOlder(identifier, created.timestamp);
previousUpdates.put(identifier, created.timestamp);
return created;
}); // TODO Refactor this spaghetti code
}
return updatedJSON.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} catch (ExecutionException e) {
throw new IllegalStateException(e);
}
}
}
public <T> JSONStorage.StoredJSON resolve(long newerThanTimestamp, DataID dataID, Supplier<T> creator) { public <T> JSONStorage.StoredJSON resolve(
long newerThanTimestamp, DataID dataID, Supplier<T> jsonCreator
) {
String identifier = dataID.name(); String identifier = dataID.name();
return getStoredOrCreateJSON(newerThanTimestamp, identifier, jsonCreator);
// Attempt to find a newer version of the json file from cache
JSONStorage.StoredJSON storedJSON = jsonStorage.fetchExactJson(identifier, newerThanTimestamp)
.orElseGet(() -> jsonStorage.fetchJsonMadeAfter(identifier, newerThanTimestamp)
.orElse(null));
if (storedJSON != null) {
return storedJSON;
} }
private <T> JSONStorage.StoredJSON getStoredOrCreateJSON(
long timestamp, String identifier, Supplier<T> jsonCreator
) {
JSONStorage.StoredJSON storedJSON = getNewFromCache(timestamp, identifier);
if (storedJSON != null) return storedJSON;
// No new enough version, let's refresh and send old version of the file // No new enough version, let's refresh and send old version of the file
Future<JSONStorage.StoredJSON> updatedJSON = scheduleJSONForUpdate(timestamp, identifier, jsonCreator);
long updateThreshold = config.get(WebserverSettings.REDUCED_REFRESH_BARRIER); storedJSON = getOldFromCache(timestamp, identifier);
// Check if the json is already being created
Future<JSONStorage.StoredJSON> updatedJSON;
accessLock.enter();
try {
updatedJSON = currentlyProcessing.get(identifier);
if (updatedJSON == null && previousUpdates.getOrDefault(identifier, 0L) < newerThanTimestamp - updateThreshold) {
// Submit a task to refresh the data if the json is old
updatedJSON = processing.submitNonCritical(() -> {
JSONStorage.StoredJSON created = jsonStorage.storeJson(identifier, creator.get());
currentlyProcessing.remove(identifier);
jsonStorage.invalidateOlder(identifier, created.timestamp);
previousUpdates.put(identifier, created.timestamp);
return created;
});
currentlyProcessing.put(identifier, updatedJSON);
}
} finally {
accessLock.exit();
}
// Get an old version from cache
storedJSON = jsonStorage.fetchJsonMadeBefore(identifier, newerThanTimestamp).orElse(null);
if (storedJSON != null) { if (storedJSON != null) {
return storedJSON; return storedJSON;
} else { } else {
// Update not performed if the last update was recent and the file is deleted before next update
// Fall back to waiting for the updated file if old version of the file doesn't exist.
if (updatedJSON == null) {
updatedJSON = submitToProcessing(identifier, jsonCreator);
}
return waitAndGetUpdated(updatedJSON);
}
}
private JSONStorage.StoredJSON waitAndGetUpdated(Future<JSONStorage.StoredJSON> updatedJSON) {
// If there is no version available, block thread until the new finishes being generated. // If there is no version available, block thread until the new finishes being generated.
try { try {
// Can be null if the last update was recent and the file is deleted before next update.
if (updatedJSON == null) {
updatedJSON = processing.submitNonCritical(() -> {
JSONStorage.StoredJSON created = jsonStorage.storeJson(identifier, creator.get());
currentlyProcessing.remove(identifier);
jsonStorage.invalidateOlder(identifier, created.timestamp);
previousUpdates.put(identifier, created.timestamp);
return created;
}); // TODO Refactor this spaghetti code
}
return updatedJSON.get(); return updatedJSON.get();
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
@ -181,6 +110,43 @@ public class AsyncJSONResolverService {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
} }
private JSONStorage.StoredJSON getOldFromCache(long newerThanTimestamp, String identifier) {
return jsonStorage.fetchJsonMadeBefore(identifier, newerThanTimestamp).orElse(null);
} }
private JSONStorage.StoredJSON getNewFromCache(long newerThanTimestamp, String identifier) {
return jsonStorage.fetchExactJson(identifier, newerThanTimestamp)
.orElseGet(() -> jsonStorage.fetchJsonMadeAfter(identifier, newerThanTimestamp)
.orElse(null));
}
private <T> Future<JSONStorage.StoredJSON> scheduleJSONForUpdate(long newerThanTimestamp, String identifier, Supplier<T> jsonCreator) {
long updateThreshold = config.get(WebserverSettings.REDUCED_REFRESH_BARRIER);
Future<JSONStorage.StoredJSON> updatedJSON;
accessLock.enter();
try {
// Check if the json is already being created
updatedJSON = currentlyProcessing.get(identifier);
if (updatedJSON == null && previousUpdates.getOrDefault(identifier, 0L) < newerThanTimestamp - updateThreshold) {
// Submit a task to refresh the data if the json is old
updatedJSON = submitToProcessing(identifier, jsonCreator);
currentlyProcessing.put(identifier, updatedJSON);
}
} finally {
accessLock.exit();
}
return updatedJSON;
}
private <T> Future<JSONStorage.StoredJSON> submitToProcessing(String identifier, Supplier<T> jsonCreator) {
return processing.submitNonCritical(() -> {
JSONStorage.StoredJSON created = jsonStorage.storeJson(identifier, jsonCreator.get());
currentlyProcessing.remove(identifier);
jsonStorage.invalidateOlder(identifier, created.timestamp);
previousUpdates.put(identifier, created.timestamp);
return created;
});
}
} }