Skip to content

Commit

Permalink
added methods for invalidating the cache
Browse files Browse the repository at this point in the history
  • Loading branch information
pwinckles committed Mar 15, 2021
1 parent 95fc3c1 commit 9aa2bb6
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,18 @@ public interface OcflRepository {
*/
OcflConfig config();

/**
* If the OcflRepository is using an inventory cache, then this method invalidates the cache entry for the
* specified object. Otherwise, nothing happens.
*
* @param objectId the ID of the object to invalidate in the cache
*/
void invalidateCache(String objectId);

/**
* If the OcflRepository is using an inventory cache, then this method invalidates all entries in the cache.
* Otherwise, nothing happens.
*/
void invalidateCache();

}
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,24 @@ public OcflConfig config() {
return new OcflConfig(config);
}

/**
* {@inheritDoc}
*/
@Override
public void invalidateCache(String objectId) {
if (objectId != null) {
storage.invalidateCache(objectId);
}
}

/**
* {@inheritDoc}
*/
@Override
public void invalidateCache() {
storage.invalidateCache();
}

protected Inventory loadInventory(ObjectVersionId objectId) {
return storage.loadInventory(objectId.getObjectId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ public interface Cache<K,V> {
*/
void invalidate(K key);

/**
* Invalidates the entire cache
*/
void invalidateAll();

/**
* Returns true if the cache contains the specified key
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ public void invalidate(K key) {
cache.invalidate(key);
}

/**
* {@inheritDoc}
*/
@Override
public void invalidateAll() {
cache.invalidateAll();
}

/**
* {@inheritDoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public void invalidate(K key) {
// no op
}

@Override
public void invalidateAll() {
// no op
}

@Override
public boolean contains(K key) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public abstract class BaseObjectDetailsDatabase implements ObjectDetailsDatabase
private final String updateDetailsQuery;
private final String insertDetailsQuery;
private final String selectDigestQuery;
private final String deleteAllQuery;

public BaseObjectDetailsDatabase(String tableName,
DataSource dataSource,
Expand Down Expand Up @@ -96,6 +97,7 @@ public BaseObjectDetailsDatabase(String tableName,
" (object_id, version_id, object_root_path, revision_id, inventory_digest, digest_algorithm, inventory, update_timestamp)" +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?)", tableName);
this.selectDigestQuery = String.format("SELECT inventory_digest FROM %s WHERE object_id = ?", tableName);
this.deleteAllQuery = String.format("DELETE FROM %s", tableName);
}

/**
Expand Down Expand Up @@ -206,6 +208,22 @@ public void deleteObjectDetails(String objectId) {
}
}

/**
* {@inheritDoc}
*/
@Override
public void deleteAllDetails() {
try (var connection = dataSource.getConnection()) {
setLockWaitTimeout(connection, waitMillis);
try (var statement = connection.prepareStatement(deleteAllQuery)) {
statement.executeUpdate();
}
} catch (SQLException e) {
throwLockException(e);
throw new OcflDbException(e);
}
}

private void updateObjectDetailsInternal(Inventory inventory, String inventoryDigest, InputStream inventoryStream, Runnable runnable) {
try (var connection = dataSource.getConnection()) {
connection.setAutoCommit(false);
Expand Down Expand Up @@ -347,6 +365,12 @@ private void throwLockException(SQLException e, String objectId) {
}
}

private void throwLockException(SQLException e) {
if (lockFailCode.equals(e.getSQLState())) {
throw new LockException("Failed to acquire lock: " + e.getMessage());
}
}

private void safeEnableAutoCommit(Connection connection) {
try {
connection.setAutoCommit(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,9 @@ public interface ObjectDetailsDatabase {
*/
void deleteObjectDetails(String objectId);

/**
* Removes all ObjectDetails from the database
*/
void deleteAllDetails();

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ public void close() {
closed = true;
}

/**
* {@inheritDoc}
*/
@Override
public void invalidateCache(String objectId) {
// no op
}

/**
* {@inheritDoc}
*/
@Override
public void invalidateCache() {
// no op
}

/**
* Does whatever is necessary to initialize OCFL repository storage.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,22 @@ public void importObject(String objectId, Path objectPath) {
public void close() {
delegate.close();
}

/**
* {@inheritDoc}
*/
@Override
public void invalidateCache(String objectId) {
inventoryCache.invalidate(objectId);
delegate.invalidateCache(objectId);
}

/**
* {@inheritDoc}
*/
@Override
public void invalidateCache() {
inventoryCache.invalidateAll();
delegate.invalidateCache();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,24 @@ public void close() {
delegate.close();
}

/**
* {@inheritDoc}
*/
@Override
public void invalidateCache(String objectId) {
objectDetailsDb.deleteObjectDetails(objectId);
delegate.invalidateCache(objectId);
}

/**
* {@inheritDoc}
*/
@Override
public void invalidateCache() {
objectDetailsDb.deleteAllDetails();
delegate.invalidateCache();
}

private void updateDetails(Inventory inventory, Path stagingDir, Runnable runnable) {
var inventoryPath = ObjectPaths.inventoryPath(stagingDir);
var sidecarPath = ObjectPaths.inventorySidecarPath(stagingDir, inventory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,18 @@ void initializeStorage(OcflVersion ocflVersion,
*/
void close();

/**
* If the OcflStorage is using an inventory cache, then this method invalidates the cache entry for the
* specified object. Otherwise, nothing happens.
*
* @param objectId the ID of the object to invalidate in the cache
*/
void invalidateCache(String objectId);

/**
* If the OcflStorage is using an inventory cache, then this method invalidates all entries in the cache.
* Otherwise, nothing happens.
*/
void invalidateCache();

}
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,38 @@ public void shouldDeleteDetailsWhenExist() {
assertNull(details);
}

@Test
public void shouldDeleteAllDetailsWhenExist() {
var inventory = basicInventory();
var invBytes = inventoryBytes(inventory);
var digest = DigestUtil.computeDigestHex(inventory.getDigestAlgorithm(), invBytes);

database.addObjectDetails(inventory, digest, invBytes);
var details = database.retrieveObjectDetails(inventory.getId());

assertObjectDetails(inventory, digest, invBytes, details);

var inv2 = Inventory.builderFromStub("o2", new OcflConfig(), "o2")
.addFileToManifest("f1", "v1/content/file1.txt")
.addHeadVersion(Version.builder()
.created(OffsetDateTime.now())
.addFile("f1", "file1.txt")
.build())
.build();
var invBytes2 = inventoryBytes(inv2);
var digest2 = DigestUtil.computeDigestHex(inventory.getDigestAlgorithm(), invBytes2);

database.addObjectDetails(inv2, digest2, invBytes2);
details = database.retrieveObjectDetails(inv2.getId());

assertObjectDetails(inv2, digest2, invBytes2, details);

database.deleteAllDetails();

assertNull(database.retrieveObjectDetails(inventory.getId()));
assertNull(database.retrieveObjectDetails(inv2.getId()));
}

@Test
public void shouldDoNothingWhenDeleteAndDetailsDoNotExist() {
database.deleteObjectDetails("o1");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.wisc.library.ocfl.itest;

import com.github.benmanes.caffeine.cache.Caffeine;
import edu.wisc.library.ocfl.api.OcflOption;
import edu.wisc.library.ocfl.api.OcflRepository;
import edu.wisc.library.ocfl.api.exception.AlreadyExistsException;
Expand All @@ -21,10 +22,12 @@
import edu.wisc.library.ocfl.api.model.VersionInfo;
import edu.wisc.library.ocfl.api.model.VersionNum;
import edu.wisc.library.ocfl.core.OcflRepositoryBuilder;
import edu.wisc.library.ocfl.core.cache.CaffeineCache;
import edu.wisc.library.ocfl.core.extension.UnsupportedExtensionBehavior;
import edu.wisc.library.ocfl.core.extension.storage.layout.config.FlatLayoutConfig;
import edu.wisc.library.ocfl.core.extension.storage.layout.config.HashedNTupleLayoutConfig;
import edu.wisc.library.ocfl.core.extension.storage.layout.config.HashedNTupleIdEncapsulationLayoutConfig;
import edu.wisc.library.ocfl.core.model.Inventory;
import edu.wisc.library.ocfl.core.path.constraint.ContentPathConstraints;
import edu.wisc.library.ocfl.core.path.mapper.LogicalPathMappers;
import edu.wisc.library.ocfl.core.storage.filesystem.FileSystemOcflStorage;
Expand All @@ -43,6 +46,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.DigestInputStream;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.List;
Expand Down Expand Up @@ -2095,6 +2099,49 @@ public void doNotFailWhenRepoContainsUnsupportedObjectExtensionAndSetToWarn() {
repo.describeObject("o2");
}

@Test
public void invalidateCacheWhenObjectExists() {
var repoName = "clear-cache";

var cache = new CaffeineCache<String, Inventory>(Caffeine.newBuilder()
.expireAfterAccess(Duration.ofMinutes(10))
.maximumSize(512).build());

var repo = defaultRepo(repoName, builder -> {
builder.inventoryCache(cache);
});

var objectId1 = "o1";
var objectId2 = "o2";

repo.updateObject(ObjectVersionId.head(objectId1), defaultVersionInfo, updater -> {
updater.writeFile(streamString("file1"), "file1.txt");
});
repo.updateObject(ObjectVersionId.head(objectId1), defaultVersionInfo, updater -> {
updater.writeFile(streamString("file2"), "file2.txt");
});
repo.updateObject(ObjectVersionId.head(objectId2), defaultVersionInfo, updater -> {
updater.writeFile(streamString("file3"), "file3.txt");
});

assertTrue(cache.contains(objectId1));
assertTrue(cache.contains(objectId2));

repo.invalidateCache(objectId1);

assertFalse(cache.contains(objectId1));
assertTrue(cache.contains(objectId2));

repo.describeObject(objectId1);

assertTrue(cache.contains(objectId1));

repo.invalidateCache();

assertFalse(cache.contains(objectId1));
assertFalse(cache.contains(objectId2));
}

private void verifyStream(Path expectedFile, OcflObjectVersionFile actual) throws IOException {
var stream = actual.getStream();
var contents = TestHelper.inputToString(stream);
Expand Down

0 comments on commit 9aa2bb6

Please sign in to comment.