Skip to content

Add clear API to HttpCache for clearing all cached data#5535

Open
fru1tworld wants to merge 4 commits into
ktorio:mainfrom
fru1tworld:fix/4719-cache-clear
Open

Add clear API to HttpCache for clearing all cached data#5535
fru1tworld wants to merge 4 commits into
ktorio:mainfrom
fru1tworld:fix/4719-cache-clear

Conversation

@fru1tworld
Copy link
Copy Markdown
Contributor

Subsystem
Client, HttpCache

Motivation
Fixes #4719. There is no way to clear the HttpCache, including the in-memory layer in CachingCacheStorage. Deleting cache files manually still leaves stale in-memory entries.

Solution
Add suspend fun clear() to CacheStorage (default no-op) with overrides in UnlimitedStorage, CachingCacheStorage, and FileCacheStorage. Expose HttpCache.clearAllCaches() so callers can do client.plugin(HttpCache).clearAllCaches().

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

📝 Walkthrough

Walkthrough

Adds suspend-based cache purge APIs: HttpCache.clearAllCaches() and CacheStorage.clear() (with JVM-default impl). Implements clear() in storage backends (UnlimitedStorage, CachingCacheStorage, FileCacheStorage) with concurrency coordination, and adds JVM tests verifying global cache clearing.

Changes

Cache clear flow

Layer / File(s) Summary
Interface / API surface
.../common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt, ktor-client/ktor-client-core/api/ktor-client-core.api, .../api/ktor-client-core.klib.api
Adds suspend fun clear() to CacheStorage (default no-op) and exposes HttpCache.clearAllCaches() in public ABI manifests.
Plugin entry
.../common/src/io/ktor/client/plugins/cache/HttpCache.kt
Adds suspend fun clearAllCaches() that validates new-storage mode, calls clear() on private and public storages, preserves cancellation semantics, and aggregates non-cancellation errors (suppresses subsequent failures).
Unlimited storage
.../common/src/io/ktor/client/plugins/cache/storage/UnlimitedCacheStorage.kt
Implements override suspend fun clear() delegating to underlying store.clear() to remove all entries.
Caching wrapper
.../jvm/src/.../CachingCacheStorage.kt
Adds mutex and serializes store, find, findAll, remove, removeAll, and new clear() via mutex.withLock so in-memory map and delegate stay consistent; implements clear() to purge both layers.
File-backed storage
.../jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt
Introduces accessGate, activeReaders, readersDrained and withReadAccess to track readers; routes ops through read gating; implements clear() to acquire exclusive access, wait for readers, delete cache files (logs failures), and clear per-URL mutexes.
Tests (JVM)
.../jvm/test/CachingCacheStorageTest.kt, .../jvm/test/FileStorageTest.kt
Adds tests verifying CachingCacheStorage.clear() clears both in-memory and delegate, and that FileCacheStorage.clear() removes file-backed entries for stored URLs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • ktorio/ktor#4915: Modifies FileCacheStorage I/O/concurrency handling; touches the same read/remove/find/findAll paths and is closely related.

Suggested labels

codex

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Add clear API to HttpCache for clearing all cached data' accurately describes the main change: introducing a clear API for HttpCache to remove all cached data.
Description check ✅ Passed The PR description follows the template with all required sections: Subsystem, Motivation, and Solution. It clearly references issue #4719 and explains the implementation approach.
Linked Issues check ✅ Passed The PR fully addresses issue #4719 by implementing a complete clear mechanism spanning interface definition, multiple storage implementations, and exposing the API through HttpCache.clearAllCaches().
Out of Scope Changes check ✅ Passed All changes are directly related to implementing cache clearing functionality as specified in issue #4719. No unrelated modifications were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
ktor-client/ktor-client-core/api/ktor-client-core.api (1)

636-690: KDoc for CacheStorage.clear() should clarify the default behavior.

The current KDoc ("Removes all entries from this cache storage") doesn't document that clear() is a default no-op. Since all known implementations (InMemoryCacheStorage, CachingCacheStorage, FileCacheStorage, UnlimitedStorage) override it with actual cleanup logic, the KDoc should explicitly state that this is a default no-op implementation and that subclasses should override it to ensure proper resource cleanup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ktor-client/ktor-client-core/api/ktor-client-core.api` around lines 636 -
690, Update the KDoc for the interface method CacheStorage.clear() to state that
the default implementation is a no-op and does not perform cleanup, and
explicitly advise implementors to override clear() to perform any necessary
resource or entry cleanup; reference the interface method CacheStorage.clear and
the provided default implementation CacheStorage$DefaultImpls.clear, and mention
common implementations (InMemoryCacheStorage, CachingCacheStorage,
FileCacheStorage, UnlimitedStorage) as examples that should implement proper
cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ktor-client/ktor-client-core/api/ktor-client-core.klib.api`:
- Line 413: clearAllCaches() currently only clears
publicStorageNew/privateStorageNew and misses legacy storages when useOldStorage
is true; to fix, add a clear() method to the legacy HttpCacheStorage
interface/implementation and update clearAllCaches() to detect useOldStorage (or
non-null publicStorage/privateStorage) and call
publicStorage.clear()/privateStorage.clear() for the legacy path, while still
clearing publicStorageNew/privateStorageNew for the modern path; also ensure
HttpCacheLegacy.kt code paths that use publicStorage/privateStorage rely on that
new clear() and mark the API change with deprecation notes for the old
properties.

In
`@ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCache.kt`:
- Around line 141-147: The clearAllCaches() implementation is only clearing
publicStorageNew/privateStorageNew and ignores legacy storages when
useOldStorage is true and also clears public before private (risking private not
being attempted on throw); update clearAllCaches() (and its KDoc) to either (A)
support legacy storages: if useOldStorage then clear privateStorage first then
publicStorage, and otherwise clear privateStorageNew then publicStorageNew,
ensuring private is attempted even if public throws, or (B) if legacy clear is
intentionally unsupported, add an explicit
check(require(!useOldStorage))/check(...) that fails fast with an actionable
message; also update the public KDoc for clearAllCaches() to include the brief
description and the standard "[Report a problem]" link.

In
`@ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt`:
- Around line 116-119: The public suspend fun clear() in the CacheStorage
interface currently has an empty KDoc and a no-op default implementation; update
its KDoc to include the standard "[Report a problem]" link and explicitly
document that the default implementation is a no-op and stateful CacheStorage
implementations must override clear() so that HttpCache.clearAllCaches()
actually clears stateful caches; after updating the KDoc and/or implementation,
run the legacy ABI update task to refresh the public API signature file.

In
`@ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt`:
- Around line 121-130: clear() currently deletes files and then clears the
mutexes map while other ops (readCache, updateCache, deleteCache) can still be
running, causing race conditions; introduce a shared synchronization primitive
(e.g., a global Mutex or a read/write lock named something like globalCacheLock)
and modify readCache, updateCache, and deleteCache to acquire a shared/read lock
(or lock.withLock) while clear() acquires the exclusive/write lock so clear()
runs serialized with all file ops; in clear(), hold that exclusive lock for the
entire deletion phase, check the boolean return of file.delete() and log/warn or
handle failures (do not silently ignore false), and only call mutexes.clear()
while still holding the exclusive lock after all deletions complete.
- Around line 74-77: The clear() implementation updates delegate and the
in-memory store in two unsynchronized steps, allowing concurrent
find/findAll/remove/removeAll to observe stale data; fix it by protecting store
and all accessors with a single coroutine Mutex (e.g., add a private val mutex =
Mutex() in FileCacheStorage) and wrap delegate/store mutations and reads in
mutex.withLock { ... } — apply withLock in clear(), find(), findAll(), remove(),
and removeAll() so clear becomes atomic with respect to other operations.

In `@ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt`:
- Line 85: The test function name testClear is not descriptive and should follow
the repository pattern using a backticked, human-readable description; rename
the function (e.g., change fun testClear() = runTest { ... } to something like
fun `clears stored files from FileStorage`() = runTest { ... }) and update any
references to that function if present (keep the runTest body unchanged and
ensure the function is top-level test function as before).

---

Nitpick comments:
In `@ktor-client/ktor-client-core/api/ktor-client-core.api`:
- Around line 636-690: Update the KDoc for the interface method
CacheStorage.clear() to state that the default implementation is a no-op and
does not perform cleanup, and explicitly advise implementors to override clear()
to perform any necessary resource or entry cleanup; reference the interface
method CacheStorage.clear and the provided default implementation
CacheStorage$DefaultImpls.clear, and mention common implementations
(InMemoryCacheStorage, CachingCacheStorage, FileCacheStorage, UnlimitedStorage)
as examples that should implement proper cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6a9b660e-e7f0-4150-a09c-f0fbf477a56d

📥 Commits

Reviewing files that changed from the base of the PR and between 8b690f4 and 2027b49.

📒 Files selected for processing (8)
  • ktor-client/ktor-client-core/api/ktor-client-core.api
  • ktor-client/ktor-client-core/api/ktor-client-core.klib.api
  • ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCache.kt
  • ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt
  • ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/UnlimitedCacheStorage.kt
  • ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt
  • ktor-client/ktor-client-core/jvm/test/CachingCacheStorageTest.kt
  • ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt

}

final class io.ktor.client.plugins.cache/HttpCache { // io.ktor.client.plugins.cache/HttpCache|null[0]
final suspend fun clearAllCaches() // io.ktor.client.plugins.cache/HttpCache.clearAllCaches|clearAllCaches(){}[0]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify clearAllCaches behavior for both new CacheStorage and legacy HttpCacheStorage paths.

rg -n -C4 'fun clearAllCaches|useOldStorage|publicStorageNew|privateStorageNew|publicStorage: HttpCacheStorage|privateStorage: HttpCacheStorage' \
  --glob '*.kt'

rg -n -C4 'abstract class HttpCacheStorage|interface CacheStorage|fun clear|override .*clear' \
  --glob '*.kt'

Repository: ktorio/ktor

Length of output: 50367


🏁 Script executed:

# Find HttpCacheStorage class definition and check for clear method
rg -n 'abstract class HttpCacheStorage|interface HttpCacheStorage|class.*HttpCacheStorage' --glob '*.kt' -A 20

Repository: ktorio/ktor

Length of output: 5879


🏁 Script executed:

# Check what methods are in HttpCacheStorage
rg -n 'class HttpCacheStorage|abstract.*HttpCacheStorage' --glob '*.kt' -A 50 | head -100

Repository: ktorio/ktor

Length of output: 7036


🏁 Script executed:

# Look for CacheStorage interface to understand the new API
rg -n 'interface CacheStorage|class.*CacheStorage' --glob '*.kt' -A 15 | grep -E '(interface|class|fun|suspend)' | head -50

Repository: ktorio/ktor

Length of output: 4129


🏁 Script executed:

# Get the full CacheStorage interface definition
cat -n ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt | sed -n '79,150p'

Repository: ktorio/ktor

Length of output: 3162


🏁 Script executed:

# Check the legacy interceptor implementations to see if they use publicStorage/privateStorage
rg -n 'fun interceptSendLegacy|fun interceptReceiveLegacy' --glob '*.kt' -A 30 | head -80

Repository: ktorio/ktor

Length of output: 37


🏁 Script executed:

# Also check if HttpCacheStorage has clear method anywhere
rg -n 'abstract fun clear|override fun clear|fun clear' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt

Repository: ktorio/ktor

Length of output: 92


🏁 Script executed:

# Search for the legacy interceptor methods in HttpCache.kt
rg -n 'interceptSendLegacy|interceptReceiveLegacy' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCache.kt -A 25

Repository: ktorio/ktor

Length of output: 2879


🏁 Script executed:

# Find the actual implementations of interceptSendLegacy and interceptReceiveLegacy
rg -n 'private.*fun interceptSendLegacy|private.*fun interceptReceiveLegacy' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCache.kt -A 40 | head -120

Repository: ktorio/ktor

Length of output: 37


🏁 Script executed:

# Search more broadly for these legacy methods
rg -n 'interceptSendLegacy|interceptReceiveLegacy' --glob '*.kt' -B 2 -A 20

Repository: ktorio/ktor

Length of output: 12198


🏁 Script executed:

# Look at cacheResponse method to see which storage it uses
rg -n 'fun cacheResponse|private.*fun cacheResponse' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCache.kt -A 20

Repository: ktorio/ktor

Length of output: 1039


🏁 Script executed:

# Check if publicStorage or privateStorage are ever READ (not just stored)
rg -n 'plugin\.publicStorage\.|plugin\.privateStorage\.|\.publicStorage\.|\.privateStorage\.' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/ --glob '*.kt' -B 2 -A 2

Repository: ktorio/ktor

Length of output: 37


🏁 Script executed:

# Check all references to publicStorage and privateStorage in the cache plugin
rg -n 'publicStorage|privateStorage' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/ --glob '*.kt'

Repository: ktorio/ktor

Length of output: 4749


🏁 Script executed:

# Get the full context of the legacy storage access in HttpCacheLegacy.kt
cat -n ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCacheLegacy.kt | sed -n '100,170p'

Repository: ktorio/ktor

Length of output: 3174


clearAllCaches() does not clear legacy cache storages when useOldStorage is enabled.

When the deprecated publicStorage or privateStorage properties are set, useOldStorage becomes true and the legacy interceptor path is activated. This path uses the deprecated HttpCacheStorage properties for all cache operations (lines 114, 120, 128, 131, 162 in HttpCacheLegacy.kt). However, clearAllCaches() only clears publicStorageNew and privateStorageNew, leaving stale entries in the actual legacy storages that are being actively read from.

Additionally, HttpCacheStorage lacks a clear() method to support clearing, making it impossible to properly support the legacy storage path without API changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ktor-client/ktor-client-core/api/ktor-client-core.klib.api` at line 413,
clearAllCaches() currently only clears publicStorageNew/privateStorageNew and
misses legacy storages when useOldStorage is true; to fix, add a clear() method
to the legacy HttpCacheStorage interface/implementation and update
clearAllCaches() to detect useOldStorage (or non-null
publicStorage/privateStorage) and call
publicStorage.clear()/privateStorage.clear() for the legacy path, while still
clearing publicStorageNew/privateStorageNew for the modern path; also ensure
HttpCacheLegacy.kt code paths that use publicStorage/privateStorage rely on that
new clear() and mark the API change with deprecation notes for the old
properties.

Comment on lines +116 to +119
/**
* Removes all entries from this cache storage.
*/
public suspend fun clear() {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the public API signature files include the new CacheStorage.clear and HttpCache.clearAllCaches APIs.
./gradlew :ktor-client:ktor-client-core:checkLegacyAbi

Repository: ktorio/ktor

Length of output: 5579


🏁 Script executed:

cat -n ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt | sed -n '100,140p'

Repository: ktorio/ktor

Length of output: 1708


🏁 Script executed:

# Check KDoc patterns in other public methods in the same file
rg -A 3 'public suspend fun' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt

Repository: ktorio/ktor

Length of output: 1112


🏁 Script executed:

# Find HttpCache.clearAllCaches() to understand the usage context
fd -t f 'HttpCache' ktor-client/ktor-client-core --type f -name '*.kt' | head -20

Repository: ktorio/ktor

Length of output: 285


🏁 Script executed:

# Check for ABI signature files in the module
find ktor-client/ktor-client-core -type f -name '*.txt' | grep -i api

Repository: ktorio/ktor

Length of output: 37


🏁 Script executed:

# Check if CacheStorage is an interface
rg -B 10 'public suspend fun clear' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt | head -20

Repository: ktorio/ktor

Length of output: 397


🏁 Script executed:

# Find HttpCache class and clearAllCaches() method
fd HttpCache.kt ktor-client/ktor-client-core --type f

Repository: ktorio/ktor

Length of output: 136


🏁 Script executed:

# Look for API signature files
find ktor-client/ktor-client-core -type d -name api

Repository: ktorio/ktor

Length of output: 154


🏁 Script executed:

# Check the class/interface declaration of CacheStorage
head -50 ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt

Repository: ktorio/ktor

Length of output: 1703


🏁 Script executed:

# Find and read HttpCache.clearAllCaches() method
rg -A 10 'fun clearAllCaches' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCache.kt

Repository: ktorio/ktor

Length of output: 478


🏁 Script executed:

# List ABI api files
ls -la ktor-client/ktor-client-core/api/

Repository: ktorio/ktor

Length of output: 324


🏁 Script executed:

# Find the new CacheStorage interface (not deprecated HttpCacheStorage)
rg 'interface CacheStorage' ktor-client/ktor-client-core/common/src

Repository: ktorio/ktor

Length of output: 183


🏁 Script executed:

# Check if clear() is in the ABI file already
grep -A 2 'public suspend fun clear' ktor-client/ktor-client-core/api/ktor-client-core.api

Repository: ktorio/ktor

Length of output: 37


🏁 Script executed:

# Verify KDoc pattern for public API by checking if clear() should have the report link
rg '\[Report a problem\]' ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt | wc -l

Repository: ktorio/ktor

Length of output: 57


🏁 Script executed:

# Check current state of ktor-client-core.api to confirm clear() absence
head -100 ktor-client/ktor-client-core/api/ktor-client-core.api | grep -i "clear\|CacheStorage"

Repository: ktorio/ktor

Length of output: 37


Add public API report link and document the default no-op behavior.

The KDoc is missing the standard [Report a problem] link that all other public methods in this interface include. Additionally, the empty default implementation means custom CacheStorage implementations that don't override clear() will silently do nothing when HttpCache.clearAllCaches() is called. Document that stateful implementations must override this method, and update the ABI signature file.

Suggested KDoc fix
     /**
      * Removes all entries from this cache storage.
+     *
+     * The default implementation is a no-op. Stateful implementations must override this method.
+     *
+     * [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.client.plugins.cache.storage.CacheStorage.clear)
      */
     public suspend fun clear() {}

Also run ./gradlew :ktor-client:ktor-client-core:updateLegacyAbi to update the public API signature file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt`
around lines 116 - 119, The public suspend fun clear() in the CacheStorage
interface currently has an empty KDoc and a no-op default implementation; update
its KDoc to include the standard "[Report a problem]" link and explicitly
document that the default implementation is a no-op and stateful CacheStorage
implementations must override clear() so that HttpCache.clearAllCaches()
actually clears stateful caches; after updating the KDoc and/or implementation,
run the legacy ABI update task to refresh the public API signature file.

Comment thread ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt Outdated
Copy link
Copy Markdown
Contributor

@bjhham bjhham left a comment

Choose a reason for hiding this comment

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

The AI had some good points regarding thread safety here. Introducing the bulk delete can complicate matters.

@fru1tworld
Copy link
Copy Markdown
Contributor Author

fru1tworld commented Apr 27, 2026

Pushed dad6f20 to address the thread-safety concern.

  • CachingCacheStorage: all operations guarded with a coroutine Mutex.
  • FileCacheStorage: clear() acquires all permits of a Semaphore(1000) so it waits for in-flight per-key operations before deleting files and resetting the mutex map.
  • HttpCache.clearAllCaches(): fail-fast on useOldStorage.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt`:
- Around line 88-93: The current pattern in FileCacheStorage.kt where clear()
tries to acquire all permits from semaphore (and relies on MAX_PERMITS) can
deadlock when two clears interleave; replace that pattern by serializing clear()
with a dedicated mutex or by using a ReentrantReadWriteLock: stop using the
“acquire all 1000 permits” approach (refer to semaphore and MAX_PERMITS) and
instead add a clearMutex (or a write lock on a ReentrantReadWriteLock) that
clear() obtains exclusively while regular file operations keep using the
existing concurrency control (or the read lock), ensuring clear() runs
one-at-a-time and removing the arbitrary MAX_PERMITS cap.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dad75b3a-7bfe-4800-a748-a9d63011326b

📥 Commits

Reviewing files that changed from the base of the PR and between 2027b49 and dad6f20.

📒 Files selected for processing (5)
  • ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCache.kt
  • ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt
  • ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt
  • ktor-client/ktor-client-core/jvm/test/CachingCacheStorageTest.kt
  • ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/storage/HttpCacheStorage.kt

@fru1tworld
Copy link
Copy Markdown
Contributor Author

Pushed fd21f10 to address the deadlock concern with concurrent clear() calls.

Added a dedicated clearMutex in FileCacheStorage so clear() runs one-at-a-time. Per-key file operations still use the semaphore for concurrency.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt (1)

88-96: Heads-up: MAX_PERMITS = 1000 is a new hard cap on concurrent file ops.

Prior to this change, FileCacheStorage had no upper bound on concurrent in-flight requests; now any 1001st concurrent store/find/findAll/remove will suspend until a permit frees. 1000 is high enough that it's unlikely to bite in practice, but the choice is unjustified in code and could surprise heavy users.

Two low-effort options worth considering:

  • Add a brief comment explaining the rationale (the cap exists solely so clear() can drain in-flight ops by acquiring all permits).
  • Or replace the semaphore-as-write-lock pattern with java.util.concurrent.locks.ReentrantReadWriteLock (read lock around per-op work, write lock in clear()), which removes the magic 1000, gives writer fairness for free, and avoids the suspending repeat(1000) { acquire() } loop in clear(). Blocking on a JDK lock is fine here since all access is already on withContext(dispatcher).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt`
around lines 88 - 96, The change introduced a hard cap MAX_PERMITS = 1000 and a
Semaphore(MAX_PERMITS) used by FileCacheStorage (semaphore, clear(), clearMutex)
which can unexpectedly throttle the 1001st concurrent file op; update the code
by adding a concise comment next to MAX_PERMITS and the semaphore explaining the
rationale (the cap exists solely so clear() can drain in-flight ops by acquiring
all permits to avoid deadlock when splitting permits) and note the tradeoffs, or
alternatively replace the semaphore/clearMutex pattern with a
java.util.concurrent.locks.ReentrantReadWriteLock (use readLock around per-op
methods like store/find/findAll/remove and writeLock in clear()) to avoid a
magic numeric cap and the repeat(1000){ acquire() } loop — reference symbols:
MAX_PERMITS, semaphore, clear(), clearMutex, FileCacheStorage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt`:
- Around line 88-96: The change introduced a hard cap MAX_PERMITS = 1000 and a
Semaphore(MAX_PERMITS) used by FileCacheStorage (semaphore, clear(), clearMutex)
which can unexpectedly throttle the 1001st concurrent file op; update the code
by adding a concise comment next to MAX_PERMITS and the semaphore explaining the
rationale (the cap exists solely so clear() can drain in-flight ops by acquiring
all permits to avoid deadlock when splitting permits) and note the tradeoffs, or
alternatively replace the semaphore/clearMutex pattern with a
java.util.concurrent.locks.ReentrantReadWriteLock (use readLock around per-op
methods like store/find/findAll/remove and writeLock in clear()) to avoid a
magic numeric cap and the repeat(1000){ acquire() } loop — reference symbols:
MAX_PERMITS, semaphore, clear(), clearMutex, FileCacheStorage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ec07ee5a-80b6-4962-af9e-46e65293dff2

📥 Commits

Reviewing files that changed from the base of the PR and between dad6f20 and fd21f10.

📒 Files selected for processing (1)
  • ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt

@fru1tworld fru1tworld marked this pull request as draft May 5, 2026 06:12
- HttpCache.clearAllCaches always attempts both private and public storages; if both fail, the first failure is thrown with the second attached as a suppressed cause. Cancellation propagates immediately.
- FileCacheStorage replaces the bounded semaphore + clearMutex pattern with a coroutine-safe read/write coordination via a single Mutex (held by clear() for its full duration) and an atomic reader counter that signals drain through a CONFLATED channel. Removes the magic 1000-permit cap.
@fru1tworld
Copy link
Copy Markdown
Contributor Author

Addressed the read-write coordination feedback in 4f1162c. PTAL.

@fru1tworld fru1tworld marked this pull request as ready for review May 11, 2026 13:23
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt (1)

145-152: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle exceptions from file.delete() to stay consistent with deleteCache and ensure mutexes.clear() runs.

File.delete() can throw (e.g., SecurityException) — deleteCache (Lines 190‑194) already catches and traces this. In clear() an unchecked exception from one file aborts the loop, skips the remaining files, and skips mutexes.clear(), leaving stale per-URL mutexes behind. Mirror the deleteCache pattern so a single problematic file does not poison the whole purge.

🛡️ Suggested fix
             val files = directory.listFiles() ?: return@withLock
             for (file in files) {
-                if (!file.delete()) {
-                    LOGGER.trace { "Failed to delete cache file: ${file.name}" }
+                try {
+                    if (!file.delete()) {
+                        LOGGER.trace { "Failed to delete cache file: ${file.name}" }
+                    }
+                } catch (cause: Exception) {
+                    LOGGER.trace { "Exception during cache deletion in a file: ${cause.stackTraceToString()}" }
                 }
             }
             mutexes.clear()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt`
around lines 145 - 152, The clear() implementation iterates files and calls
file.delete() without catching exceptions, so a thrown SecurityException can
abort deletion and prevent mutexes.clear() from running; mirror the
deleteCache(...) pattern by wrapping each file.delete() call in a try/catch that
logs via LOGGER.trace (including the exception) on failure and continues,
ensuring the loop completes and mutexes.clear() always executes; locate the
clear() function in FileCacheStorage.kt and use the same logging/message style
used by deleteCache to keep behavior consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In
`@ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt`:
- Around line 145-152: The clear() implementation iterates files and calls
file.delete() without catching exceptions, so a thrown SecurityException can
abort deletion and prevent mutexes.clear() from running; mirror the
deleteCache(...) pattern by wrapping each file.delete() call in a try/catch that
logs via LOGGER.trace (including the exception) on failure and continues,
ensuring the loop completes and mutexes.clear() always executes; locate the
clear() function in FileCacheStorage.kt and use the same logging/message style
used by deleteCache to keep behavior consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 65400abf-3557-4b81-b828-2cac461b40d2

📥 Commits

Reviewing files that changed from the base of the PR and between fd21f10 and 4f1162c.

📒 Files selected for processing (2)
  • ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCache.kt
  • ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/cache/HttpCache.kt

Copy link
Copy Markdown
Contributor

@bjhham bjhham left a comment

Choose a reason for hiding this comment

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

Looks good, thanks!

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.

No way to clear the cache

2 participants