Skip to content

Commit b867daf

Browse files
committed
Fix caching issue in composite-spaces
Changes in the base space were not immediately reflected in the composite-space. Also fix test class UpdateSpaceWithExtensionApiIT, so that it always deletes the composite-space before deleting the base-space. Signed-off-by: Benjamin Rögner <benjamin.roegner@here.com>
1 parent 9eac149 commit b867daf

File tree

5 files changed

+110
-42
lines changed

5 files changed

+110
-42
lines changed

xyz-hub-service/src/main/java/com/here/xyz/hub/connectors/models/Space.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ public class Space extends com.here.xyz.models.hub.Space implements Cloneable {
8383
@JsonIgnore
8484
private Map<ConnectorType, Map<String, List<ResolvableListenerConnectorRef>>> resolvedConnectorRefs;
8585

86+
public static Future<Space> resolveSpace(Marker marker, String spaceId) {
87+
return Service.spaceConfigClient.get(marker, spaceId);
88+
}
89+
8690
public static Future<Connector> resolveConnector(Marker marker, String connectorId) {
8791
Promise<Connector> p = Promise.promise();
8892
resolveConnector(marker, connectorId, p);
@@ -290,4 +294,13 @@ public Map<String,Object> asMap() {
290294
return Collections.emptyMap();
291295
}
292296
}
297+
298+
/**
299+
* Used for logging purposes.
300+
* @return
301+
*/
302+
@Override
303+
public String toString() {
304+
return Json.encode(this);
305+
}
293306
}

xyz-hub-service/src/main/java/com/here/xyz/hub/task/FeatureTask.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
2626

2727
import com.fasterxml.jackson.core.JsonProcessingException;
28+
import com.google.common.hash.Hasher;
2829
import com.google.common.hash.Hashing;
2930
import com.here.xyz.events.CountFeaturesEvent;
3031
import com.here.xyz.events.DeleteFeaturesByTagEvent;
@@ -81,6 +82,11 @@ public abstract class FeatureTask<T extends Event<?>, X extends FeatureTask<T, ?
8182
*/
8283
public Space space;
8384

85+
/**
86+
* The spaces being extended by {@link #space} (if existing).
87+
*/
88+
public List<Space> extendedSpaces;
89+
8490
/**
8591
* The storage connector to be used for this operation.
8692
*/
@@ -144,13 +150,15 @@ public String getCacheKey() {
144150
}
145151
try {
146152
//noinspection UnstableApiUsage
147-
cacheKey = Hashing.murmur3_128().newHasher()
153+
Hasher hasher = Hashing.murmur3_128().newHasher()
148154
.putString(getEvent().getCacheString(), Charset.defaultCharset())
149155
.putString(responseType.toString(), Charset.defaultCharset())
150-
.putLong(space.contentUpdatedAt)
151-
.hash()
152-
.toString();
153-
return cacheKey;
156+
.putLong(space.contentUpdatedAt);
157+
158+
if (space.getExtension() != null)
159+
extendedSpaces.forEach(extendedSpace -> hasher.putLong(extendedSpace.getContentUpdatedAt()));
160+
161+
return cacheKey = hasher.hash().toString();
154162
} catch (JsonProcessingException e) {
155163
return null;
156164
}

xyz-hub-service/src/main/java/com/here/xyz/hub/task/FeatureTaskHandler.java

Lines changed: 76 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
import com.here.xyz.models.geojson.implementation.FeatureCollection;
8787
import com.here.xyz.models.geojson.implementation.FeatureCollection.ModificationFailure;
8888
import com.here.xyz.models.geojson.implementation.XyzNamespace;
89+
import com.here.xyz.models.hub.Space.Extension;
8990
import com.here.xyz.responses.BinaryResponse;
9091
import com.here.xyz.responses.CountResponse;
9192
import com.here.xyz.responses.ErrorResponse;
@@ -98,9 +99,11 @@
9899
import com.here.xyz.responses.SuccessResponse;
99100
import com.here.xyz.responses.XyzResponse;
100101
import io.vertx.core.AsyncResult;
102+
import io.vertx.core.CompositeFuture;
101103
import io.vertx.core.Future;
102104
import io.vertx.core.Handler;
103105
import io.vertx.core.MultiMap;
106+
import io.vertx.core.Promise;
104107
import io.vertx.core.buffer.Buffer;
105108
import io.vertx.core.http.Cookie;
106109
import io.vertx.core.json.DecodeException;
@@ -746,65 +749,103 @@ private static EventNotification createNotification(NotificationContext nc, Payl
746749
* Resolves the space, its storage and its listeners.
747750
*/
748751
static <X extends FeatureTask> void resolveSpace(final X task, final Callback<X> callback) {
752+
try {
753+
resolveSpace(task)
754+
.compose(space -> CompositeFuture.all(
755+
resolveStorageConnector(task),
756+
resolveListenersAndProcessors(task),
757+
resolveExtendedSpaces(task, space)
758+
))
759+
.onFailure(t -> callback.exception(t))
760+
.onSuccess(connector -> callback.call(task));
761+
}
762+
catch (Exception e) {
763+
callback.exception(new HttpException(INTERNAL_SERVER_ERROR, "Unable to load the resource definition.", e));
764+
}
765+
}
766+
767+
private static <X extends FeatureTask> Future<Space> resolveSpace(final X task) {
749768
try {
750769
//FIXME: Can be removed once the Space events are handled by the SpaceTaskHandler (refactoring pending ...)
751-
if (task.space != null) { //If the space is already given we don't need to retrieve it
752-
onSpaceResolved(task, callback);
753-
return;
754-
}
770+
if (task.space != null) //If the space is already given we don't need to retrieve it
771+
return Future.succeededFuture(task.space);
755772

756773
//Load the space definition.
757-
Service.spaceConfigClient.get(task.getMarker(), task.getEvent().getSpace())
758-
.onFailure(t -> {
759-
logger.warn(task.getMarker(), "Unable to load the space definition for space '{}' {}", task.getEvent().getSpace(), t);
760-
callback.exception(new HttpException(INTERNAL_SERVER_ERROR, "Unable to load the resource definition", t));
761-
})
762-
.onSuccess(space -> {
763-
task.space = space;
764-
if (task.space != null)
765-
task.getEvent().setParams(task.space.getStorage().getParams());
766-
onSpaceResolved(task, callback);
767-
});
774+
return Space.resolveSpace(task.getMarker(), task.getEvent().getSpace())
775+
.compose(
776+
space -> {
777+
task.space = space;
778+
if (space != null)
779+
task.getEvent().setParams(space.getStorage().getParams());
780+
return Future.succeededFuture(space);
781+
},
782+
t -> {
783+
logger.warn(task.getMarker(), "Unable to load the space definition for space '{}' {}", task.getEvent().getSpace(), t);
784+
return Future.failedFuture(new HttpException(INTERNAL_SERVER_ERROR, "Unable to load the resource definition", t));
785+
}
786+
);
768787
}
769788
catch (Exception e) {
770-
callback.exception(new HttpException(INTERNAL_SERVER_ERROR, "Unable to load the resource definition.", e));
789+
return Future.failedFuture(new HttpException(INTERNAL_SERVER_ERROR, "Unable to load the resource definition.", e));
771790
}
772791
}
773792

774-
private static <X extends FeatureTask> void onSpaceResolved(final X task, final Callback<X> callback) {
775-
if (task.space == null) {
776-
callback.exception(new HttpException(NOT_FOUND, "The resource with this ID does not exist."));
777-
return;
778-
}
779-
logger.debug(task.getMarker(), "Given space configuration is: {}", Json.encode(task.space));
793+
private static <X extends FeatureTask> Future<Space> resolveExtendedSpaces(X task, Space extendingSpace) {
794+
if (extendingSpace == null)
795+
return Future.succeededFuture();
796+
return resolveExtendedSpace(task, extendingSpace.getExtension());
797+
}
780798

781-
final String storageId = task.space.getStorage().getId();
782-
AbstractHttpServerVerticle.addStreamInfo(task.context, "SID", storageId);
783-
Space.resolveConnector(task.getMarker(), storageId, (arStorage) -> {
784-
if (arStorage.failed()) {
785-
callback.exception(new InvalidStorageException("Unable to load the definition for this storage."));
786-
return;
787-
}
788-
task.storage = arStorage.result();
789-
onStorageResolved(task, callback);
790-
});
799+
private static <X extends FeatureTask> Future<Space> resolveExtendedSpace(X task, Extension spaceExtension) {
800+
if (spaceExtension == null)
801+
return Future.succeededFuture();
802+
return Space.resolveSpace(task.getMarker(), spaceExtension.getSpaceId())
803+
.compose(
804+
extendedSpace -> {
805+
if (task.extendedSpaces == null)
806+
task.extendedSpaces = new ArrayList();
807+
task.extendedSpaces.add(extendedSpace);
808+
return resolveExtendedSpace(task, extendedSpace.getExtension()); //Go to next extension level
809+
},
810+
t -> Future.failedFuture(t)
811+
);
791812
}
792813

793-
private static <X extends FeatureTask> void onStorageResolved(final X task, final Callback<X> callback) {
814+
private static <X extends FeatureTask> Future<Connector> resolveStorageConnector(final X task) {
815+
if (task.space == null)
816+
return Future.failedFuture(new HttpException(NOT_FOUND, "The resource with this ID does not exist."));
817+
818+
logger.debug(task.getMarker(), "Given space configuration is: {}", task.space);
819+
820+
final String storageId = task.space.getStorage().getId();
821+
AbstractHttpServerVerticle.addStreamInfo(task.context, "SID", storageId);
822+
return Space.resolveConnector(task.getMarker(), storageId)
823+
.compose(
824+
connector -> {
825+
task.storage = connector;
826+
return Future.succeededFuture(connector);
827+
},
828+
t -> Future.failedFuture(new InvalidStorageException("Unable to load the definition for this storage."))
829+
);
830+
}
831+
832+
private static <X extends FeatureTask> Future<Void> resolveListenersAndProcessors(final X task) {
833+
Promise<Void> p = Promise.promise();
794834
try {
795835
//Also resolve all listeners & processors
796836
CompletableFuture.allOf(
797837
resolveConnectors(task.getMarker(), task.space, ConnectorType.LISTENER),
798838
resolveConnectors(task.getMarker(), task.space, ConnectorType.PROCESSOR)
799839
).thenRun(() -> {
800840
//All listener & processor refs have been resolved now
801-
callback.call(task);
841+
p.complete();
802842
});
803843
}
804844
catch (Exception e) {
805845
logger.error(task.getMarker(), "The listeners for this space cannot be initialized", e);
806-
callback.exception(new HttpException(INTERNAL_SERVER_ERROR, "The listeners for this space cannot be initialized"));
846+
p.fail(new HttpException(INTERNAL_SERVER_ERROR, "The listeners for this space cannot be initialized"));
807847
}
848+
return p.future();
808849
}
809850

810851
private static CompletableFuture<Void> resolveConnectors(Marker marker, final Space space, final ConnectorType connectorType) {

xyz-hub-test/src/test/java/com/here/xyz/hub/rest/TTLTestsIT.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ public void testCacheWithGeoJson() {
7979
.then()
8080
.extract().body().path("features[0].id");
8181

82+
try {
83+
//Wait 1s to give the asynchronous cache-write some time.
84+
Thread.sleep(1_000);
85+
}
86+
catch (InterruptedException e) {}
87+
8288
String id_cached = given()
8389
.headers(getAuthHeaders(AuthProfile.ACCESS_ALL))
8490
.contentType(APPLICATION_JSON)

xyz-hub-test/src/test/java/com/here/xyz/hub/rest/UpdateSpaceWithExtensionApiIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ public class UpdateSpaceWithExtensionApiIT extends TestSpaceWithFeature {
3333

3434
@BeforeClass
3535
public static void setupClass() {
36-
removeSpace("x-psql-test");
3736
removeSpace("x-psql-test-extension");
37+
removeSpace("x-psql-test");
3838
createSpace();
3939
createSpaceWithExtension();
4040
}
4141

4242
@AfterClass
4343
public static void tearDownClass() {
44-
removeSpace("x-psql-test");
4544
removeSpace("x-psql-test-extension");
45+
removeSpace("x-psql-test");
4646
}
4747

4848
private static void createSpaceWithExtension() {

0 commit comments

Comments
 (0)