From 397de41d032e38d43072e1e7c3fe9634d94a0b29 Mon Sep 17 00:00:00 2001 From: Gary Tierney Date: Fri, 28 Oct 2022 11:27:04 +0100 Subject: [PATCH] Avoid communicating with S3 unless necessary Previously the S3 source would eagerly connect to S3 retrieve information about the object being requested when creating a stream. This change defers retrieval of properties from S3 until needed by the S3StreamFactory, and allows cached files to be served directly from disk with no intermediate S3 lookups. --- .../cantaloupe/source/S3ObjectInfoSupplier.java | 11 +++++++++++ .../illinois/library/cantaloupe/source/S3Source.java | 8 +++++--- .../library/cantaloupe/source/S3StreamFactory.java | 6 ++++-- .../cantaloupe/source/S3StreamFactoryTest.java | 2 +- 4 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 src/main/java/edu/illinois/library/cantaloupe/source/S3ObjectInfoSupplier.java diff --git a/src/main/java/edu/illinois/library/cantaloupe/source/S3ObjectInfoSupplier.java b/src/main/java/edu/illinois/library/cantaloupe/source/S3ObjectInfoSupplier.java new file mode 100644 index 000000000..f48a9651b --- /dev/null +++ b/src/main/java/edu/illinois/library/cantaloupe/source/S3ObjectInfoSupplier.java @@ -0,0 +1,11 @@ +package edu.illinois.library.cantaloupe.source; + +import java.io.IOException; + +/** + * Deferred accessor to {@link S3ObjectInfo}. + */ +@FunctionalInterface +public interface S3ObjectInfoSupplier { + S3ObjectInfo get() throws IOException; +} diff --git a/src/main/java/edu/illinois/library/cantaloupe/source/S3Source.java b/src/main/java/edu/illinois/library/cantaloupe/source/S3Source.java index 93db8621f..67ac60fe3 100644 --- a/src/main/java/edu/illinois/library/cantaloupe/source/S3Source.java +++ b/src/main/java/edu/illinois/library/cantaloupe/source/S3Source.java @@ -422,9 +422,11 @@ private S3ObjectInfo getObjectInfoUsingDelegateStrategy() @Override public StreamFactory newStreamFactory() throws IOException { - S3ObjectInfo info = getObjectInfo(); - info.setLength(getObjectAttributes().length); - return new S3StreamFactory(info); + return new S3StreamFactory(() -> { + S3ObjectInfo info = getObjectInfo(); + info.setLength(getObjectAttributes().length); + return info; + }); } @Override diff --git a/src/main/java/edu/illinois/library/cantaloupe/source/S3StreamFactory.java b/src/main/java/edu/illinois/library/cantaloupe/source/S3StreamFactory.java index d08a0af76..d27d92db3 100644 --- a/src/main/java/edu/illinois/library/cantaloupe/source/S3StreamFactory.java +++ b/src/main/java/edu/illinois/library/cantaloupe/source/S3StreamFactory.java @@ -24,14 +24,15 @@ class S3StreamFactory implements StreamFactory { private static final int DEFAULT_CHUNK_SIZE = 1024 * 512; private static final int DEFAULT_CHUNK_CACHE_SIZE = 1024 * 1024 * 10; - private S3ObjectInfo objectInfo; + private S3ObjectInfoSupplier objectInfo; - S3StreamFactory(S3ObjectInfo objectInfo) { + S3StreamFactory(S3ObjectInfoSupplier objectInfo) { this.objectInfo = objectInfo; } @Override public InputStream newInputStream() throws IOException { + final S3ObjectInfo objectInfo = this.objectInfo.get(); final InputStream responseStream = S3Source.newObjectInputStream(objectInfo); @@ -100,6 +101,7 @@ public ImageInputStream newSeekableStream() throws IOException { LOGGER.debug("newSeekableStream(): using {}-byte chunks", chunkSize); + final S3ObjectInfo objectInfo = this.objectInfo.get(); final S3HTTPImageInputStreamClient client = new S3HTTPImageInputStreamClient(objectInfo); diff --git a/src/test/java/edu/illinois/library/cantaloupe/source/S3StreamFactoryTest.java b/src/test/java/edu/illinois/library/cantaloupe/source/S3StreamFactoryTest.java index c1432a948..3a8082cc3 100644 --- a/src/test/java/edu/illinois/library/cantaloupe/source/S3StreamFactoryTest.java +++ b/src/test/java/edu/illinois/library/cantaloupe/source/S3StreamFactoryTest.java @@ -109,7 +109,7 @@ public void setUp() throws Exception { info.setKey(FIXTURE_KEY); info.setLength(1584); - instance = new S3StreamFactory(info); + instance = new S3StreamFactory(() -> info); } @Test