You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by he...@apache.org on 2023/05/16 01:19:13 UTC

[iotdb] branch tiered_storage updated: add cache write

This is an automated email from the ASF dual-hosted git repository.

heiming pushed a commit to branch tiered_storage
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/tiered_storage by this push:
     new 9d47e76916b add cache write
9d47e76916b is described below

commit 9d47e76916b15ac87ecc26d1d17acaa424a2f3a3
Author: HeimingZ <zh...@qq.com>
AuthorDate: Tue May 16 09:18:39 2023 +0800

    add cache write
---
 .../apache/iotdb/os/cache/CacheFileManager.java    | 79 ++++++++++++++++++++++
 .../{OSFileCacheKey.java => IOSFileCache.java}     | 13 ++--
 .../org/apache/iotdb/os/cache/OSFileCache.java     | 58 +++++++++++++---
 .../org/apache/iotdb/os/cache/OSFileCacheKey.java  | 53 ++++++++++++++-
 .../apache/iotdb/os/cache/OSFileCacheValue.java    | 22 +++---
 .../iotdb/os/cache/RemoteFileCacheLoader.java      | 52 --------------
 .../org/apache/iotdb/os/fileSystem/OSFile.java     |  9 ++-
 .../apache/iotdb/os/fileSystem/OSTsFileInput.java  |  4 +-
 .../java/org/apache/iotdb/os/fileSystem/OSURI.java |  5 +-
 .../apache/iotdb/os/io/ObjectStorageConnector.java |  2 +-
 .../iotdb/os/io/aws/S3ObjectStorageConnector.java  | 22 ++++--
 11 files changed, 235 insertions(+), 84 deletions(-)

diff --git a/object-storage/src/main/java/org/apache/iotdb/os/cache/CacheFileManager.java b/object-storage/src/main/java/org/apache/iotdb/os/cache/CacheFileManager.java
new file mode 100644
index 00000000000..6dd208ac174
--- /dev/null
+++ b/object-storage/src/main/java/org/apache/iotdb/os/cache/CacheFileManager.java
@@ -0,0 +1,79 @@
+/*
+ * 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.iotdb.os.cache;
+
+import org.apache.iotdb.os.conf.ObjectStorageConfig;
+import org.apache.iotdb.os.conf.ObjectStorageDescriptor;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.StandardOpenOption;
+import java.util.concurrent.atomic.AtomicLong;
+
+/** This class manages all io operations to the cache files */
+public class CacheFileManager {
+  private static final Logger logger = LoggerFactory.getLogger(CacheFileManager.class);
+  private static final String CACHE_FILE_SUFFIX = ".cache";
+  private static final String TMP_CACHE_FILE_SUFFIX = ".cache.tmp";
+  private static final ObjectStorageConfig config =
+      ObjectStorageDescriptor.getInstance().getConfig();
+  private String[] cacheDirs;
+  private AtomicLong logFileId = new AtomicLong(0);
+
+  public CacheFileManager(String[] cacheDirs) {
+    this.cacheDirs = cacheDirs;
+  }
+
+  private long getNextCacheFileId() {
+    return logFileId.getAndIncrement();
+  }
+
+  private File getTmpCacheFile(long id) {
+    long dirId = id % cacheDirs.length;
+    return new File(cacheDirs[(int) dirId], id + TMP_CACHE_FILE_SUFFIX);
+  }
+
+  private File getCacheFile(long id) {
+    long dirId = id % cacheDirs.length;
+    return new File(cacheDirs[(int) dirId], id + CACHE_FILE_SUFFIX);
+  }
+
+  /** Persist data, return null when failing to persist data */
+  public OSFileCacheValue persist(OSFileCacheKey key, byte[] data) {
+    OSFileCacheValue res = null;
+    long cacheFileId = getNextCacheFileId();
+    File tmpCacheFile = getTmpCacheFile(cacheFileId);
+    try (FileChannel channel =
+        FileChannel.open(tmpCacheFile.toPath(), StandardOpenOption.CREATE_NEW)) {
+      ByteBuffer meta = key.serializeToByteBuffer();
+      channel.write(meta);
+      channel.write(ByteBuffer.wrap(data));
+      res = new OSFileCacheValue(tmpCacheFile, meta.capacity(), data.length);
+    } catch (IOException e) {
+      logger.error("Fail to persist data to cache file {}", tmpCacheFile, e);
+      tmpCacheFile.delete();
+    }
+    return tmpCacheFile.renameTo(getCacheFile(cacheFileId)) ? res : null;
+  }
+}
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheKey.java b/object-storage/src/main/java/org/apache/iotdb/os/cache/IOSFileCache.java
similarity index 71%
copy from object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheKey.java
copy to object-storage/src/main/java/org/apache/iotdb/os/cache/IOSFileCache.java
index 8757483432c..d780083ff66 100644
--- a/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheKey.java
+++ b/object-storage/src/main/java/org/apache/iotdb/os/cache/IOSFileCache.java
@@ -18,9 +18,14 @@
  */
 package org.apache.iotdb.os.cache;
 
-import org.apache.iotdb.os.fileSystem.OSFile;
+import org.apache.iotdb.os.fileSystem.OSURI;
 
-public class OSFileCacheKey {
-  private OSFile file;
-  private long position;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
+
+public interface IOSFileCache {
+  InputStream getAsInputSteam(OSURI file, long startPosition) throws IOException;
+
+  FileChannel getLocalCacheFileChannel(OSURI file, long startPosition) throws IOException;
 }
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCache.java b/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCache.java
index a2447758463..1525f382838 100644
--- a/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCache.java
+++ b/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCache.java
@@ -21,24 +21,46 @@ package org.apache.iotdb.os.cache;
 
 import org.apache.iotdb.os.conf.ObjectStorageConfig;
 import org.apache.iotdb.os.conf.ObjectStorageDescriptor;
+import org.apache.iotdb.os.fileSystem.OSURI;
+import org.apache.iotdb.os.io.ObjectStorageConnector;
+import org.apache.iotdb.os.io.aws.S3ObjectStorageConnector;
 
+import com.github.benmanes.caffeine.cache.CacheLoader;
 import com.github.benmanes.caffeine.cache.Caffeine;
 import com.github.benmanes.caffeine.cache.LoadingCache;
 import com.github.benmanes.caffeine.cache.Weigher;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
 
-public class OSFileCache {
+public class OSFileCache implements IOSFileCache {
   private static final Logger logger = LoggerFactory.getLogger(OSFileCache.class);
   private static final ObjectStorageConfig config =
       ObjectStorageDescriptor.getInstance().getConfig();
+  private static final ObjectStorageConnector connector;
 
+  static {
+    switch (config.getOsType()) {
+      case AWS_S3:
+        connector = new S3ObjectStorageConnector();
+        break;
+      default:
+        connector = null;
+    }
+  }
+
+  /**
+   * persistent LRU cache for remote TsFile, value is loaded successfully when it has been stored on
+   * the disk
+   */
   private LoadingCache<OSFileCacheKey, OSFileCacheValue> remotePos2LocalCacheFile;
+  /** manage all io operations to the cache files */
+  private CacheFileManager cacheFileManager = new CacheFileManager(config.getCacheDirs());
 
   private OSFileCache() {
     remotePos2LocalCacheFile =
@@ -46,21 +68,41 @@ public class OSFileCache {
             .maximumWeight(config.getCacheMaxDiskUsage())
             .weigher(
                 (Weigher<OSFileCacheKey, OSFileCacheValue>)
-                    (key, value) -> Math.toIntExact(value.getOccupiedLength()))
-            .build(new RemoteFileCacheLoader());
+                    (key, value) -> value.getOccupiedLength())
+            .removalListener(
+                (key, value, cuase) -> {
+                  if (value != null) {
+                    value.clear();
+                  }
+                })
+            .build(new OSFileCacheLoader());
   }
 
-  public InputStream getAsInputSteam(String fileName, long startPosition) throws IOException {
-    FileChannel cacheFileChannel = getLocalCacheFileChannel(fileName, startPosition);
-    return Channels.newInputStream(cacheFileChannel);
+  @Override
+  public InputStream getAsInputSteam(OSURI file, long startPosition) throws IOException {
+    return null;
   }
 
-  private FileChannel getLocalCacheFileChannel(String fileName, long startPosition) {
+  @Override
+  public FileChannel getLocalCacheFileChannel(OSURI file, long startPosition) {
     // 根据 fileName 和 startPosition 计算出对应的本地文件路径,并返回对应的 FileChannel
     // 如果是使用一个 CacheFile, 则寻找到对应的位置,可能需要封装一个自己的 FileChannel 防止读多
     return null;
   }
 
+  void put(OSFileCacheKey key, OSFileCacheValue value) {
+    remotePos2LocalCacheFile.put(key, value);
+  }
+
+  class OSFileCacheLoader implements CacheLoader<OSFileCacheKey, OSFileCacheValue> {
+    @Override
+    public @Nullable OSFileCacheValue load(@NonNull OSFileCacheKey key) throws Exception {
+      byte[] data =
+          connector.getRemoteFile(key.getFile().toOSURI(), key.getStartPosition(), key.getLength());
+      return cacheFileManager.persist(key, data);
+    }
+  }
+
   public static OSFileCache getInstance() {
     return OSFileCache.InstanceHolder.INSTANCE;
   }
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheKey.java b/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheKey.java
index 8757483432c..ff157872635 100644
--- a/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheKey.java
+++ b/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheKey.java
@@ -20,7 +20,56 @@ package org.apache.iotdb.os.cache;
 
 import org.apache.iotdb.os.fileSystem.OSFile;
 
-public class OSFileCacheKey {
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+public class OSFileCacheKey implements Serializable {
   private OSFile file;
-  private long position;
+  private long startPosition;
+  private int length;
+
+  public OSFileCacheKey(OSFile file, long startPosition, int length) {
+    this.file = file;
+    this.startPosition = startPosition;
+    this.length = length;
+  }
+
+  public ByteBuffer serializeToByteBuffer() {
+    byte[] pathBytes = file.toString().getBytes();
+    ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + pathBytes.length + Integer.BYTES);
+    buffer.putInt(pathBytes.length);
+    buffer.put(pathBytes);
+    buffer.putInt(length);
+    return buffer;
+  }
+
+  public OSFile getFile() {
+    return file;
+  }
+
+  public long getStartPosition() {
+    return startPosition;
+  }
+
+  public int getLength() {
+    return length;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(file, startPosition, length);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    OSFileCacheKey that = (OSFileCacheKey) obj;
+    return file.equals(that.file) && startPosition == that.startPosition && length == that.length;
+  }
 }
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheValue.java b/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheValue.java
index 70df017d0a2..e9afbacacd8 100644
--- a/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheValue.java
+++ b/object-storage/src/main/java/org/apache/iotdb/os/cache/OSFileCacheValue.java
@@ -19,36 +19,42 @@
 
 package org.apache.iotdb.os.cache;
 
+import java.io.File;
+
 public class OSFileCacheValue {
-  private String fileName;
+  private File cacheFile;
   // 如果每个块用一个文件来存储,则该值一直为 0
   // 如果使用一个大文件存储所有块,则该值为大文件中的起点
   private long startPosition;
   // 如果每个块用一个文件来存储,则该值一直为该文件的大小
   // 如果使用一个大文件存储所有块,则该值为该块的实际长度
-  private long length;
+  private int length;
 
-  public OSFileCacheValue(String fileName, long startPosition, long length) {
-    this.fileName = fileName;
+  public OSFileCacheValue(File cacheFile, long startPosition, int length) {
+    this.cacheFile = cacheFile;
     this.startPosition = startPosition;
     this.length = length;
   }
 
-  public String getFileName() {
-    return fileName;
+  public File getCacheFile() {
+    return cacheFile;
   }
 
   public long getStartPosition() {
     return startPosition;
   }
 
-  public long getLength() {
+  public int getLength() {
     return length;
   }
 
-  public long getOccupiedLength() {
+  public int getOccupiedLength() {
     // 如果使用多个文件,则返回该文件的大小
     // 如果使用一个文件,则返回每个槽的大小
     return length;
   }
+
+  public void clear() {
+    cacheFile.delete();
+  }
 }
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/cache/RemoteFileCacheLoader.java b/object-storage/src/main/java/org/apache/iotdb/os/cache/RemoteFileCacheLoader.java
deleted file mode 100644
index 9d6702f7440..00000000000
--- a/object-storage/src/main/java/org/apache/iotdb/os/cache/RemoteFileCacheLoader.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.iotdb.os.cache;
-
-import org.apache.iotdb.os.conf.ObjectStorageConfig;
-import org.apache.iotdb.os.conf.ObjectStorageDescriptor;
-import org.apache.iotdb.os.io.ObjectStorageConnector;
-import org.apache.iotdb.os.io.aws.S3ObjectStorageConnector;
-
-import com.github.benmanes.caffeine.cache.CacheLoader;
-import org.checkerframework.checker.nullness.qual.NonNull;
-import org.checkerframework.checker.nullness.qual.Nullable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class RemoteFileCacheLoader implements CacheLoader<OSFileCacheKey, OSFileCacheValue> {
-  private static final Logger logger = LoggerFactory.getLogger(RemoteFileCacheLoader.class);
-  private static final ObjectStorageConfig config =
-      ObjectStorageDescriptor.getInstance().getConfig();
-  private static final ObjectStorageConnector connector;
-
-  static {
-    switch (config.getOsType()) {
-      case AWS_S3:
-        connector = new S3ObjectStorageConnector();
-        break;
-      default:
-        connector = null;
-    }
-  }
-
-  @Override
-  public @Nullable OSFileCacheValue load(@NonNull OSFileCacheKey key) throws Exception {
-    return null;
-  }
-}
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSFile.java b/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSFile.java
index 64bff215538..9f61ef431d7 100644
--- a/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSFile.java
+++ b/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSFile.java
@@ -355,7 +355,10 @@ public class OSFile extends File {
 
   @Override
   public boolean equals(Object obj) {
-    if (!(obj instanceof OSFile)) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
       return false;
     }
     OSFile other = (OSFile) obj;
@@ -377,6 +380,10 @@ public class OSFile extends File {
     throw new UnsupportedOperationException(UNSUPPORT_OPERATION);
   }
 
+  public OSURI toOSURI() {
+    return osUri;
+  }
+
   public BufferedReader getBufferedReader() {
     try {
       return new BufferedReader(new InputStreamReader(connector.getInputStream(osUri)));
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSTsFileInput.java b/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSTsFileInput.java
index 18df7c4e270..c42bc104651 100644
--- a/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSTsFileInput.java
+++ b/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSTsFileInput.java
@@ -33,7 +33,7 @@ import java.nio.channels.FileChannel;
 public class OSTsFileInput implements TsFileInput {
   private static final Logger logger = LoggerFactory.getLogger(OSTsFileInput.class);
 
-  private String osFileName;
+  private OSFile file;
   private OSFileCache cache = OSFileCache.getInstance();
 
   @Override
@@ -101,6 +101,6 @@ public class OSTsFileInput implements TsFileInput {
   }
 
   public InputStream getNextInputStream(long position) throws IOException {
-    return cache.getAsInputSteam(osFileName, position);
+    return cache.getAsInputSteam(file.toOSURI(), position);
   }
 }
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSURI.java b/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSURI.java
index 28e6b03a5cb..66a5b10424e 100644
--- a/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSURI.java
+++ b/object-storage/src/main/java/org/apache/iotdb/os/fileSystem/OSURI.java
@@ -62,7 +62,10 @@ public class OSURI {
   }
 
   public boolean equals(Object obj) {
-    if (!(obj instanceof OSURI)) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
       return false;
     }
     OSURI other = (OSURI) obj;
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/io/ObjectStorageConnector.java b/object-storage/src/main/java/org/apache/iotdb/os/io/ObjectStorageConnector.java
index cbf004329e6..c396eaeb816 100644
--- a/object-storage/src/main/java/org/apache/iotdb/os/io/ObjectStorageConnector.java
+++ b/object-storage/src/main/java/org/apache/iotdb/os/io/ObjectStorageConnector.java
@@ -42,5 +42,5 @@ public interface ObjectStorageConnector {
 
   void putLocalFile(OSURI osUri, File lcoalFile) throws ObjectStorageException;
 
-  byte[] getRemoteFile(OSURI osUri, long position, int size) throws ObjectStorageException;
+  byte[] getRemoteFile(OSURI osUri, long position, int len) throws ObjectStorageException;
 }
diff --git a/object-storage/src/main/java/org/apache/iotdb/os/io/aws/S3ObjectStorageConnector.java b/object-storage/src/main/java/org/apache/iotdb/os/io/aws/S3ObjectStorageConnector.java
index 92cfa90d767..699ffda13d2 100644
--- a/object-storage/src/main/java/org/apache/iotdb/os/io/aws/S3ObjectStorageConnector.java
+++ b/object-storage/src/main/java/org/apache/iotdb/os/io/aws/S3ObjectStorageConnector.java
@@ -24,6 +24,7 @@ import org.apache.iotdb.os.fileSystem.OSURI;
 import org.apache.iotdb.os.io.IMetaData;
 import org.apache.iotdb.os.io.ObjectStorageConnector;
 
+import software.amazon.awssdk.core.ResponseBytes;
 import software.amazon.awssdk.core.sync.RequestBody;
 import software.amazon.awssdk.regions.Region;
 import software.amazon.awssdk.services.s3.S3Client;
@@ -31,6 +32,7 @@ import software.amazon.awssdk.services.s3.model.CopyObjectRequest;
 import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
 import software.amazon.awssdk.services.s3.model.DeleteObjectResponse;
 import software.amazon.awssdk.services.s3.model.GetObjectRequest;
+import software.amazon.awssdk.services.s3.model.GetObjectResponse;
 import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
 import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
 import software.amazon.awssdk.services.s3.model.ListObjectsRequest;
@@ -42,6 +44,7 @@ import java.io.File;
 import java.io.InputStream;
 
 public class S3ObjectStorageConnector implements ObjectStorageConnector {
+  private static final String RANGE_FORMAT = "%d-%d";
   private S3Client s3Client;
 
   public S3ObjectStorageConnector() {
@@ -161,11 +164,20 @@ public class S3ObjectStorageConnector implements ObjectStorageConnector {
   }
 
   @Override
-  public byte[] getRemoteFile(OSURI osUri, long position, int size) throws ObjectStorageException {
-    GetObjectRequest req =
-        GetObjectRequest.builder().bucket(osUri.getBucket()).key(osUri.getKey()).build();
-    s3Client.getObject(req);
-    return new byte[0];
+  public byte[] getRemoteFile(OSURI osUri, long position, int len) throws ObjectStorageException {
+    String rangeStr = String.format(RANGE_FORMAT, position, position + len - 1);
+    try {
+      GetObjectRequest req =
+          GetObjectRequest.builder()
+              .bucket(osUri.getBucket())
+              .key(osUri.getKey())
+              .range(rangeStr)
+              .build();
+      ResponseBytes<GetObjectResponse> resp = s3Client.getObjectAsBytes(req);
+      return resp.asByteArray();
+    } catch (S3Exception e) {
+      throw new ObjectStorageException(e);
+    }
   }
 
   public void close() {