Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify ArchiveManager archived entry cache #1022

Open
wants to merge 6 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/freenet/client/ArchiveBucketCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package freenet.client;

import freenet.keys.FreenetURI;
import freenet.support.LRUMap;
import freenet.support.api.Bucket;
import freenet.support.io.MultiReaderBucket;

class ArchiveBucketCache {
/**
* Maximum number of cached buckets.
*/
private final int maxBuckets;

/**
* Maximum cached data in bytes.
*/
private final long maxBytes;

/**
* Underlying cached items by cache key (as [uri]:[filename]).
*/
private final LRUMap<String, CachedBucket> cache = new LRUMap<>();

/**
* Currently cached data in bytes.
*/
private long currentBytes;

ArchiveBucketCache(int maxBuckets, long maxBytes) {
this.maxBuckets = maxBuckets;
this.maxBytes = maxBytes;
}

/**
* Add an item to the cache and return a claimed bucket for the same item.
* The returned bucket (if not null) must be freed by the caller.
*
* @return a read-only bucket referencing the cached data, or null if the entry was not found
*/
synchronized Bucket acquire(FreenetURI uri, String filename) {
String key = createCacheKey(uri, filename);
CachedBucket item = cache.get(key);
if (item != null) {
// Promote the item to the top of the LRU.
cache.push(key, item);
// Acquire the bucket while holding lock to ensure the item is not yet released.
return acquire(item);
}
return null;
}

/**
* Add an item to the cache and return an acquired bucket for the same item.
* The cache assumes responsibility of freeing the provided bucket, the caller should not free it.
* The returned bucket must eventually be freed by the caller.
*
* @return a read-only bucket referencing the same data as the provided bucket
*/
synchronized Bucket addAndAcquire(FreenetURI uri, String filename, Bucket bucket) {
// Store the item in the cache, it will be released when it gets evicted from the cache.
String key = createCacheKey(uri, filename);
CachedBucket item = new CachedBucket(bucket);
CachedBucket oldItem = cache.push(key, item);
onAdded(item);

// Acquire the bucket now to keep the item alive, even if the item is evicted immediately after we return.
Bucket acquired = acquire(item);

// Cleanup the evicted item (if any) and evict the least recently items to stay within size limits.
onEvicted(oldItem);
evictLeastRecentlyUsedItems();

return acquired;
}

private void evictLeastRecentlyUsedItems() {
while (!cache.isEmpty() && (currentBytes > maxBytes || cache.size() > maxBuckets)) {
CachedBucket oldItem = cache.popValue();
onEvicted(oldItem);
}
}

private void onAdded(CachedBucket item) {
currentBytes += item.size;
}

private void onEvicted(CachedBucket item) {
if (item != null) {
currentBytes -= item.size;
item.keepAliveReference.free();
}
}

private static Bucket acquire(CachedBucket item) {
return item.multiReaderBucket.getReaderBucket();
}

private static String createCacheKey(FreenetURI uri, String filename) {
return uri.toASCIIString() + ":" + filename;
}

private static class CachedBucket {
private final MultiReaderBucket multiReaderBucket;
private final Bucket keepAliveReference;
private final long size;

private CachedBucket(Bucket bucket) {
this.multiReaderBucket = new MultiReaderBucket(bucket);
this.keepAliveReference = multiReaderBucket.getReaderBucket();
this.size = bucket.size();
}
}
}
4 changes: 3 additions & 1 deletion src/freenet/client/ArchiveExtractCallback.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public interface ArchiveExtractCallback extends Serializable {
public void notInArchive(ClientContext context);

/** Failed: restart */
public void onFailed(ArchiveRestartException e, ClientContext context);
@Deprecated
default void onFailed(ArchiveRestartException e, ClientContext context) {
}

/** Failed for some other reason */
public void onFailed(ArchiveFailureException e, ClientContext context);
Expand Down
37 changes: 10 additions & 27 deletions src/freenet/client/ArchiveHandlerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,51 +31,34 @@ class ArchiveHandlerImpl implements ArchiveHandler, Serializable {
}

@Override
public Bucket get(String internalName, ArchiveContext archiveContext,
ArchiveManager manager)
throws ArchiveFailureException, ArchiveRestartException,
MetadataParseException, FetchException {

if(forceRefetchArchive) return null;

Bucket data;

// Fetch from cache
if(logMINOR)
Logger.minor(this, "Checking cache: "+key+ ' ' +internalName);
if((data = manager.getCached(key, internalName)) != null) {
return data;
public Bucket get(String internalName, ArchiveContext archiveContext, ArchiveManager manager) {
if (forceRefetchArchive) {
return null;
}

return null;
if (logMINOR) {
Logger.minor(this, "Checking cache: " + key + ' ' + internalName);
}
return manager.getCached(key, internalName);
}

@Override
public Bucket getMetadata(ArchiveContext archiveContext,
ArchiveManager manager) throws ArchiveFailureException,
ArchiveRestartException, MetadataParseException, FetchException {
public Bucket getMetadata(ArchiveContext archiveContext, ArchiveManager manager) {
return get(".metadata", archiveContext, manager);
}

@Override
public void extractToCache(Bucket bucket, ArchiveContext actx,
String element, ArchiveExtractCallback callback,
ArchiveManager manager, ClientContext context) throws ArchiveFailureException,
ArchiveRestartException {
ArchiveManager manager, ClientContext context) throws ArchiveFailureException {
forceRefetchArchive = false; // now we don't need to force refetch any more
ArchiveStoreContext ctx = manager.makeContext(key, archiveType, compressorType, false);
manager.extractToCache(key, archiveType, compressorType, bucket, actx, ctx, element, callback, context);
manager.extractToCache(key, archiveType, compressorType, bucket, actx, element, callback, context);
}

@Override
public ARCHIVE_TYPE getArchiveType() {
return archiveType;
}

public COMPRESSOR_TYPE getCompressorType() {
return compressorType;
}

@Override
public FreenetURI getKey() {
return key;
Expand Down
36 changes: 0 additions & 36 deletions src/freenet/client/ArchiveKey.java

This file was deleted.

Loading