org.dinky.shaded.paimon.io.cache.CacheManager Maven / Gradle / Ivy
The newest version!
/*
* 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.dinky.shaded.paimon.io.cache;
import org.dinky.shaded.paimon.annotation.VisibleForTesting;
import org.dinky.shaded.paimon.memory.MemorySegment;
import org.dinky.shaded.paimon.options.MemorySize;
import org.dinky.shaded.paimon.shade.caffeine2.com.github.benmanes.caffeine.cache.Cache;
import org.dinky.shaded.paimon.shade.caffeine2.com.github.benmanes.caffeine.cache.Caffeine;
import org.dinky.shaded.paimon.shade.caffeine2.com.github.benmanes.caffeine.cache.RemovalCause;
import org.dinky.shaded.paimon.shade.guava30.com.google.common.util.concurrent.MoreExecutors;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Objects;
import java.util.function.Consumer;
/** Cache manager to cache bytes to paged {@link MemorySegment}s. */
public class CacheManager {
private final int pageSize;
private final Cache cache;
public CacheManager(int pageSize, MemorySize maxMemorySize) {
this.pageSize = pageSize;
this.cache =
Caffeine.newBuilder()
.weigher(this::weigh)
.maximumWeight(maxMemorySize.getBytes())
.removalListener(this::onRemoval)
.executor(MoreExecutors.directExecutor())
.build();
}
@VisibleForTesting
Cache cache() {
return cache;
}
public int pageSize() {
return pageSize;
}
public MemorySegment getPage(
RandomAccessFile file, int pageNumber, Consumer cleanCallback) {
CacheKey key = new CacheKey(file, pageNumber);
CacheValue value = cache.getIfPresent(key);
while (value == null || value.isClosed) {
try {
value = createValue(key, cleanCallback);
} catch (IOException e) {
throw new RuntimeException(e);
}
cache.put(key, value);
}
return value.segment;
}
public void invalidPage(RandomAccessFile file, int pageNumber) {
cache.invalidate(new CacheKey(file, pageNumber));
}
private int weigh(CacheKey cacheKey, CacheValue cacheValue) {
return cacheValue.segment.size();
}
private void onRemoval(CacheKey key, CacheValue value, RemovalCause cause) {
value.isClosed = true;
value.cleanCallback.accept(key.pageNumber);
}
private CacheValue createValue(CacheKey key, Consumer cleanCallback)
throws IOException {
return new CacheValue(key.read(pageSize), cleanCallback);
}
private static class CacheKey {
private final RandomAccessFile file;
private final int pageNumber;
private CacheKey(RandomAccessFile file, int pageNumber) {
this.file = file;
this.pageNumber = pageNumber;
}
private MemorySegment read(int pageSize) throws IOException {
long length = file.length();
long pageAddress = (long) pageNumber * pageSize;
int len = (int) Math.min(pageSize, length - pageAddress);
byte[] bytes = new byte[len];
file.seek(pageAddress);
file.readFully(bytes);
return MemorySegment.wrap(bytes);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CacheKey cacheKey = (CacheKey) o;
return pageNumber == cacheKey.pageNumber && Objects.equals(file, cacheKey.file);
}
@Override
public int hashCode() {
return Objects.hash(file, pageNumber);
}
}
private static class CacheValue {
private final MemorySegment segment;
private final Consumer cleanCallback;
private boolean isClosed = false;
private CacheValue(MemorySegment segment, Consumer cleanCallback) {
this.segment = segment;
this.cleanCallback = cleanCallback;
}
}
}