Skip to content

feature(cache): caching API - #636

Merged
Rongmario merged 14 commits into
mainfrom
feature/caching-api
Oct 12, 2026
Merged

Rongmario merged 14 commits into
mainfrom
feature/caching-api

Conversation

@Rongmario

Copy link
Copy Markdown
Member

Track the proposal here: #627

Summary

This proposal + PR adds a file cache in the com.cleanroommc.cleanroom.cache package for downloaded, bundled and generated files.
An entry is fetched once, verified by SHA-256 on every access, kept across restarts, and served offline once cached.

Usage

Path jar = Cache.global().namespace("mod_id")
        .fromUrl(URI.create("https://example.com/dep-4.0.0.jar"))
        .version("4.0.0")
        .expectSha256(hex)
        .get("dep.jar");

Cache.instance().namespace("properties")
        .fromString("k=1", StandardCharsets.UTF_8)
        .put("default.properties");

API

Type Members
Cache instance(), global(), namespace(String), root(), fromUrl, fromResource, fromFile, fromString, fromBytes, fromBuffer
CacheRequest expectSha256, version, revalidateIf, get, put, getAsync, putAsync
CacheException kind() returns OFFLINE, HASH_MISMATCH, HTTP or UNSAFE_PATH
  • get ingests only when the entry is missing, corrupt, outdated, or expected with another hash.
  • put always ingests from the source.
  • The returned path changes when the content changes.

Behaviour

Integrity

  • A hash passed to expectSha256 is enforced. Without one, the first ingested hash is pinned.
  • Every get re-hashes the stored file.
  • Corrupt content is restored from the source and must match the pinned hash.

Confinement

  • Absolute paths, drive letters, UNC prefixes, NUL bytes and .. escapes are rejected.
  • Symbolic links anywhere between the cache root and the entry are rejected.
  • The .meta, .lock and .versions suffixes are reserved on every path segment.

Versioning

  • version(String) re-ingests when the requested version is greater, compared with Forge's ComparableVersion.
  • An outdated entry that cannot refresh offline throws OFFLINE, naming the cached and the required version.

Web

  • Uses the JDK HttpClient with redirects, a 10s connect timeout, and a 30s timeout for response headers and for each body read.
  • revalidateIf(Duration) serves the cached entry and revalidates it in the background with a conditional GET (ETag, Last-Modified).
  • A failed revalidation keeps the entry and waits a full period before retrying.
  • An offline cold miss throws OFFLINE. It never leaves an empty file.

Concurrency

  • Each entry has a lock within the JVM, plus a file lock on <name>.lock for other processes.
  • Downloads run without holding either lock. Concurrent gets of one entry share a single download.

Filesystem Layout

<namespace>/
  <name>.lock -> file lock
  <name>.meta -> sha256, version, url, etag, modified, checked
  <name>.versions/
    <16 hex chars>/
      <name>.jar -> content

System Properties

Property Default
cleanroom.cache.root.instance <gameDir>/.cleanroom/cache
cleanroom.cache.root.global ~/.cleanroom/cache
cleanroom.offline false
cleanroom.cache.timeout 30 (seconds)

@Rongmario Rongmario added this to the Cleanroom 0.7.0 milestone Sep 25, 2026
@Rongmario Rongmario self-assigned this Sep 25, 2026

@Kuba663 Kuba663 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly just there to be there.

Comment thread module/cleanroom/src/main/java/com/cleanroommc/cleanroom/cache/Cache.java Outdated
Comment thread module/cleanroom/src/main/java/com/cleanroommc/cleanroom/cache/Cache.java Outdated
Comment thread module/cleanroom/src/main/java/com/cleanroommc/cleanroom/cache/Cache.java Outdated
Comment thread module/cleanroom/src/main/java/com/cleanroommc/cleanroom/cache/Cache.java Outdated

@Kuba663 Kuba663 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good from my end.

@Rongmario
Rongmario merged commit 7faec0a into main Oct 12, 2026
1 of 2 checks passed
@Rongmario
Rongmario deleted the feature/caching-api branch October 12, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants