Repository: ktorio/ktor
File: ktor-client/ktor-client-core/jvm/src/io/ktor/client/plugins/cache/storage/FileCacheStorage.kt (Kotlin)

Code before:
                    val channel = it.toByteReadChannel()
                    val requestsCount = channel.readInt()
                    val caches = mutableSetOf<CachedResponseData>()
                    for (i in 0 until requestsCount) {
                        caches.add(readCache(channel))
                    }
                    channel.discard()
                    return caches
                }
            } catch (cause: Exception) {
                LOGGER.trace("Exception during cache lookup in a file: ${cause.stackTraceToString()}")
                return emptySet()
            }
        }
    }

    private suspend fun deleteCache(urlHex: String) {
        val mutex = mutexes.computeIfAbsent(urlHex) { Mutex() }
        mutex.withLock {
            val file = File(directory, urlHex)
            if (!file.exists()) return

            try {
                file.delete()
            } catch (cause: Exception) {
                LOGGER.trace("Exception during cache deletion in a file: ${cause.stackTraceToString()}")
            }
        }
    }

    private suspend fun writeCache(channel: ByteChannel, cache: CachedResponseData) {
        channel.writeStringUtf8(cache.url.toString() + "\n")
        channel.writeInt(cache.statusCode.value)
        channel.writeStringUtf8(cache.statusCode.description + "\n")
        channel.writeStringUtf8(cache.version.toString() + "\n")
        val headers = cache.headers.flattenEntries()
        channel.writeInt(headers.size)
        for ((key, value) in headers) {
            channel.writeStringUtf8(key + "\n")
            channel.writeStringUtf8(value + "\n")
        }
        channel.writeLong(cache.requestTime.timestamp)
        channel.writeLong(cache.responseTime.timestamp)
        channel.writeLong(cache.expires.timestamp)
        channel.writeInt(cache.varyKeys.size)
        for ((key, value) in cache.varyKeys) {
            channel.writeStringUtf8(key + "\n")
            channel.writeStringUtf8(value + "\n")
        }
        channel.writeInt(cache.body.size)
        channel.writeFully(cache.body)

Review comment (bug):
Minor. Let's use trace with lazy message here:
```suggestion
                LOGGER.trace { "Exception during cache deletion in a file: ${cause.stackTraceToString()}" }
```
@coderabbitai, remember that we prefer `fun Logger.trace(message: () -> String)` and `fun Logger.debug(message: () -> String)` with lazy message evaluation to avoid unnecessary allocations.

Code after:
            }
        }
    }

    private suspend fun writeCacheUnsafe(urlHex: String, caches: List<CachedResponseData>) = coroutineScope {
        val channel = ByteChannel()
        try {
            File(directory, urlHex).outputStream().buffered().use { output ->
                launch {
                    channel.writeInt(caches.size)
                    for (cache in caches) {
                        writeCache(channel, cache)
                    }
                    channel.close()
                }
                channel.copyTo(output)
            }
        } catch (cause: Exception) {
            LOGGER.trace { "Exception during saving a cache to a file: ${cause.stackTraceToString()}" }
        }
    }

    private suspend fun readCacheUnsafe(urlHex: String): Set<CachedResponseData> {
        val file = File(directory, urlHex)
        if (!file.exists()) return emptySet()

        try {
            file.inputStream().buffered().use {
                val channel = it.toByteReadChannel()
                val requestsCount = channel.readInt()
                val caches = mutableSetOf<CachedResponseData>()
                for (i in 0 until requestsCount) {
                    caches.add(readCache(channel))
                }
                channel.discard()
                return caches
            }
        } catch (cause: Exception) {
            LOGGER.trace { "Exception during cache lookup in a file: ${cause.stackTraceToString()}" }
            return emptySet()
        }
    }

    private suspend fun writeCache(channel: ByteChannel, cache: CachedResponseData) {
        channel.writeStringUtf8(cache.url.toString() + "\n")
        channel.writeInt(cache.statusCode.value)
        channel.writeStringUtf8(cache.statusCode.description + "\n")
        channel.writeStringUtf8(cache.version.toString() + "\n")
        val headers = cache.headers.flattenEntries()
        channel.writeInt(headers.size)
        for ((key, value) in headers) {