[NO ISSUE][*DB][STO] Support a no-op cloud storage scheme (none)
When configuring a cloud.storage.scheme of none, do not actually
perform reads or writes to a cloud provider. All reads will be empty
and writes will not actually be performed. Additionally, do not
reconcile local cache with the cloud during recovery, since the
cloud does not have anything stored.
Ext-ref: MB-65877
Change-Id: Ib2911f35177d2acde3ce1e98f94e9a2384df16ab
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/19536
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Michael Blow <mblow@apache.org>
Reviewed-by: Michael Blow <mblow@apache.org>
Reviewed-by: Hussain Towaileb <hussainht@gmail.com>
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/api/common/LocalCloudUtilAdobeMock.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/api/common/LocalCloudUtilAdobeMock.java
index 95cdb1c..1aefadf 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/api/common/LocalCloudUtilAdobeMock.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/api/common/LocalCloudUtilAdobeMock.java
@@ -103,6 +103,7 @@
} catch (Exception ex) {
// do nothing
}
+ s3Mock = null;
}
}
}
diff --git a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/AbstractCloudIOManager.java b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/AbstractCloudIOManager.java
index e4224f2..3e5622e 100644
--- a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/AbstractCloudIOManager.java
+++ b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/AbstractCloudIOManager.java
@@ -87,6 +87,7 @@
protected final IOManager localIoManager;
protected final INamespacePathResolver nsPathResolver;
private final List<FileStore> drivePaths;
+ private final String storageScheme;
public AbstractCloudIOManager(IOManager ioManager, CloudProperties cloudProperties,
INamespacePathResolver nsPathResolver, ICloudGuardian guardian) throws HyracksDataException {
@@ -102,6 +103,7 @@
partitionPaths = new ArrayList<>();
this.localIoManager = ioManager;
drivePaths = PhysicalDrive.getDrivePaths(ioDevices);
+ storageScheme = cloudProperties.getStorageScheme();
}
/*
@@ -143,6 +145,11 @@
partitionPaths.add(resolve(STORAGE_ROOT_DIR_NAME + File.separator + partitionDir));
}
+ if (CloudClientProvider.NONE.equals(storageScheme)) {
+ LOGGER.info("Cloud storage scheme is '{}', nothing to reconcile / download", storageScheme);
+ return;
+ }
+
LOGGER.info("Initializing cloud manager with ({}) storage partitions: {}", partitions.size(), partitions);
if (!currentOnDiskPartitions.isEmpty()) {
deleteUnkeptPartitionDirs(currentOnDiskPartitions);
diff --git a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/CloudResettableInputStream.java b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/CloudResettableInputStream.java
index 9b832a2..ab8c6c4 100644
--- a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/CloudResettableInputStream.java
+++ b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/CloudResettableInputStream.java
@@ -71,11 +71,6 @@
*/
@Override
- public int write(ByteBuffer header, ByteBuffer page) throws HyracksDataException {
- return write(header) + write(page);
- }
-
- @Override
public int write(ByteBuffer page) throws HyracksDataException {
open();
return write(page.array(), page.position(), page.remaining());
diff --git a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/CloudClientProvider.java b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/CloudClientProvider.java
index c98c6b4..03a5b46 100644
--- a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/CloudClientProvider.java
+++ b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/CloudClientProvider.java
@@ -33,6 +33,7 @@
public static final String S3 = "s3";
public static final String GCS = "gs";
public static final String AZ_BLOB = "azblob";
+ public static final String NONE = "none";
private CloudClientProvider() {
throw new AssertionError("do not instantiate");
@@ -41,19 +42,13 @@
public static ICloudClient getClient(CloudProperties cloudProperties, ICloudGuardian guardian)
throws HyracksDataException {
String storageScheme = cloudProperties.getStorageScheme();
- ICloudClient cloudClient;
- if (S3.equalsIgnoreCase(storageScheme)) {
- S3ClientConfig config = S3ClientConfig.of(cloudProperties);
- cloudClient = new S3CloudClient(config, guardian);
- } else if (GCS.equalsIgnoreCase(storageScheme)) {
- GCSClientConfig config = GCSClientConfig.of(cloudProperties);
- cloudClient = new GCSCloudClient(config, guardian);
- } else if (AZ_BLOB.equalsIgnoreCase(storageScheme)) {
- AzBlobStorageClientConfig config = AzBlobStorageClientConfig.of(cloudProperties);
- cloudClient = new AzBlobStorageCloudClient(config, guardian);
- } else {
- throw new IllegalStateException("unsupported cloud storage scheme: " + storageScheme);
- }
+ ICloudClient cloudClient = switch (storageScheme.toLowerCase()) {
+ case S3 -> new S3CloudClient(S3ClientConfig.of(cloudProperties), guardian);
+ case GCS -> new GCSCloudClient(GCSClientConfig.of(cloudProperties), guardian);
+ case AZ_BLOB -> new AzBlobStorageCloudClient(AzBlobStorageClientConfig.of(cloudProperties), guardian);
+ case NONE -> NoopCloudClient.INSTANCE;
+ default -> throw new IllegalStateException("unsupported cloud storage scheme: " + storageScheme);
+ };
return UNSTABLE ? new UnstableCloudClient(cloudClient) : cloudClient;
}
diff --git a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/ICloudWriter.java b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/ICloudWriter.java
index 920be9c..5299b5d 100644
--- a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/ICloudWriter.java
+++ b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/ICloudWriter.java
@@ -33,7 +33,9 @@
* @param page to write
* @return written bytes
*/
- int write(ByteBuffer header, ByteBuffer page) throws HyracksDataException;
+ default int write(ByteBuffer header, ByteBuffer page) throws HyracksDataException {
+ return write(header) + write(page);
+ }
/**
* Write a page
diff --git a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/NoopCloudClient.java b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/NoopCloudClient.java
new file mode 100644
index 0000000..fb83aa9
--- /dev/null
+++ b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/NoopCloudClient.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.cloud.clients;
+
+import java.io.FilenameFilter;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.asterix.cloud.IWriteBufferProvider;
+import org.apache.asterix.cloud.clients.profiler.IRequestProfilerLimiter;
+import org.apache.asterix.cloud.clients.profiler.NoOpRequestProfilerLimiter;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.io.FileReference;
+import org.apache.hyracks.control.nc.io.IOManager;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class NoopCloudClient implements ICloudClient {
+
+ public static final ICloudClient INSTANCE = new NoopCloudClient();
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ private NoopCloudClient() {
+ }
+
+ @Override
+ public void close() throws HyracksDataException {
+ // no-op
+ }
+
+ @Override
+ public int getWriteBufferSize() {
+ return 0;
+ }
+
+ @Override
+ public IRequestProfilerLimiter getProfilerLimiter() {
+ return NoOpRequestProfilerLimiter.INSTANCE;
+ }
+
+ @Override
+ public ICloudWriter createWriter(String bucket, String path, IWriteBufferProvider bufferProvider) {
+ return new NoOpCloudWriter();
+ }
+
+ @Override
+ public Set<CloudFile> listObjects(String bucket, String path, FilenameFilter filter) {
+ return Set.of();
+ }
+
+ @Override
+ public int read(String bucket, String path, long offset, ByteBuffer buffer) throws HyracksDataException {
+ return 0;
+ }
+
+ @Override
+ public byte[] readAllBytes(String bucket, String path) throws HyracksDataException {
+ return new byte[0];
+ }
+
+ @Override
+ public InputStream getObjectStream(String bucket, String path, long offset, long length) {
+ return InputStream.nullInputStream();
+ }
+
+ @Override
+ public void write(String bucket, String path, byte[] data) {
+ }
+
+ @Override
+ public void copy(String bucket, String srcPath, FileReference destPath) {
+ }
+
+ @Override
+ public void deleteObjects(String bucket, Collection<String> paths) throws HyracksDataException {
+ }
+
+ @Override
+ public long getObjectSize(String bucket, String path) throws HyracksDataException {
+ return 0;
+ }
+
+ @Override
+ public boolean exists(String bucket, String path) throws HyracksDataException {
+ return false;
+ }
+
+ @Override
+ public boolean isEmptyPrefix(String bucket, String path) throws HyracksDataException {
+ return false;
+ }
+
+ @Override
+ public IParallelDownloader createParallelDownloader(String bucket, IOManager ioManager)
+ throws HyracksDataException {
+ return NoOpParallelDownloader.INSTANCE;
+ }
+
+ @Override
+ public JsonNode listAsJson(ObjectMapper objectMapper, String bucket) {
+ return OBJECT_MAPPER.createArrayNode();
+ }
+
+ private static class NoOpCloudWriter implements ICloudWriter {
+
+ long position = 0L;
+
+ public NoOpCloudWriter() {
+ }
+
+ @Override
+ public void abort() throws HyracksDataException {
+ position = 0L;
+ }
+
+ @Override
+ public int write(ByteBuffer page) throws HyracksDataException {
+ int written = page.remaining();
+ position += written;
+ page.position(page.limit());
+ return written;
+ }
+
+ @Override
+ public void write(int b) throws HyracksDataException {
+ position++;
+ }
+
+ @Override
+ public int write(byte[] b, int off, int len) throws HyracksDataException {
+ position += len;
+ return len;
+ }
+
+ @Override
+ public long position() {
+ return position;
+ }
+
+ @Override
+ public void finish() throws HyracksDataException {
+ position = 0;
+ }
+ }
+
+ private static class NoOpParallelDownloader implements IParallelDownloader {
+ private static final NoOpParallelDownloader INSTANCE = new NoOpParallelDownloader();
+
+ @Override
+ public void close() throws HyracksDataException {
+ }
+
+ @Override
+ public void downloadFiles(Collection<FileReference> toDownload) throws HyracksDataException {
+ }
+
+ @Override
+ public Collection<FileReference> downloadDirectories(Collection<FileReference> toDownload)
+ throws HyracksDataException {
+ return List.of();
+ }
+ }
+}
diff --git a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/UnstableCloudClient.java b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/UnstableCloudClient.java
index 1b39251..cb24650 100644
--- a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/UnstableCloudClient.java
+++ b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/UnstableCloudClient.java
@@ -170,11 +170,6 @@
}
@Override
- public int write(ByteBuffer header, ByteBuffer page) throws HyracksDataException {
- return write(header) + write(page);
- }
-
- @Override
public int write(ByteBuffer page) throws HyracksDataException {
if (position() == 0) {
fail();
diff --git a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/google/gcs/GCSWriter.java b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/google/gcs/GCSWriter.java
index 3a83786..04b2de1 100644
--- a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/google/gcs/GCSWriter.java
+++ b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/clients/google/gcs/GCSWriter.java
@@ -58,11 +58,6 @@
}
@Override
- public int write(ByteBuffer header, ByteBuffer page) throws HyracksDataException {
- return write(header) + write(page);
- }
-
- @Override
public int write(ByteBuffer page) throws HyracksDataException {
guardian.checkIsolatedWriteAccess(bucket, path);
// The GCS library triggers a new upload when its internal buffer is full, not on each call to writer.write().