Fork channel

Create a new channel as a copy of main.

Rename channel

Rename main to:

Delete channel

Delete main? This cannot be undone.

CacheService.kt
/**
 * Dracon - An IntelliJ-Pijul integration.
 * Copyright 2021 JonathanxD <jhrldev@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package com.github.jonathanxd.dracon.cache

import com.intellij.openapi.components.Service
import java.time.Instant

/**
 * This is a base interface implemented by every [DataCache] service,
 * as [DataCache] is never used directly, an [IntelliJ Light Service][Service]
 * must be setup to interface a [DataCache] and classes that uses the cache.
 *
 * This interface is for [Cache][DataCache] [Services][Service] which manages a single [DataCache][cache]
 * with a [key type][K] [K] and a [value type][V] [V]. If the service does manages more than one [DataCache][cache],
 * we **heavily recommend** you to split it into two different services which communicate with each other.
 */
interface CacheService<K: Any, V: Any> {
    val cache: ActorDataCache<K, V>

    /**
     * Executes an operation in [cache] using the **cache write lock**.
     *
     * This does not guarantee absolute lock over the file caching mechanism.
     */
    fun <R> withLock(f: () -> R): R =
        this.cache.withLock(f)

    /**
     * Invalidates the [cache] that this service handles.
     *
     * ## **Warning**
     *
     * **This is only recommended when the cache is not working correctly, corrupted or an operation that changes
     * all the data that was previously cached takes in place.**
     *
     * **This is not recommended to be called for little changes, such as the ones that applies to only one or `N{2,}` keys
     * because it slow down the perception of the Dracon performance, as it does need to cache all the data again, and as the
     * cache is backed by a temporary file in the seconday storage (not a memory one), it does depends on the I/O
     * overhead, which is good for NVMe storages and SATA 3 ones, but not for HDDs. Also some `pijul` operations are really
     * expensive, such as `pijul unrecord`, the Dracon Log Algorithm, Dracon File Status algorithms and Dracon Revision
     * load algorithm (backed by `pijul unrecord`), when the cache of these operations is invalidated, the overall
     * performance Dracon drops absurdly.**
     *
     * @see DataCache
     */
    fun invalidate() {
        this.cache.invalidate()
    }

    /**
     * Invalidates one key in [cache], this is a good way to invalidate data in cache (but not the best one), as it does not
     * cause all the cache to be recomputed, however, if you already know the new value for the cache, or a function
     * to compute it, use [updateCache] function.
     *
     */
    fun invalidate(key: K) {
        this.cache.invalidate(key)
    }

    /**
     * Invalidates multiple keys in [cache], this is a good way to invalidate data in cache (but not the best one), as it does not
     * cause all the cache to be recomputed, however, if you already know the new value for the cache, or a function
     * to compute it, use [updateCache] function.
     *
     */
    fun invalidateAll(keys: Iterable<K>) {
        this.cache.invalidateAll(keys)
    }

    /**
     * Updates the cache value of a [key] using the [newValueCompute]. This is the best way to update the cache data,
     * as it does provides either a lightweight function to compute the new cache value, or a constant computed value
     * that does not involve a new computation routine.
     *
     * Please note that this function does not guarantee that the new computed value will be inserted into the cache,
     * because the cache could be updated between the time you change something that need to be reflected in the cache,
     * and the time request to update the key, if a cache recompute operation takes in this time between, the
     * service will ignore the provided [newValueCompute]. The up-to-date check is made using [CachedValue] container,
     * which holds the [Instant] which provides the time that an operation requested to update the cache (not the time the
     * update takes place). However this check do not guarantee that the cached value will be up-to-date, as we cannot guarantee
     * that the time the operation was requested matches the time the data was created.
     */
    fun updateCache(key: K, newValueCompute: () -> V) {
        this.cache.updateCache(key, newValueCompute)
    }

    /**
     * Updates the cache value of multiple [keys] using the [newValueCompute]. This is the best way to update the cache data,
     * as it does provides either a lightweight function to compute the new cache value, or a constant computed value
     * that does not involve a new computation routine.
     *
     * Please note that this function does not guarantee that the new computed value will be inserted into the cache,
     * because the cache could be updated between the time you change something that need to be reflected in the cache,
     * and the time request to update the key, if a cache recompute operation takes in this time between, the
     * service will ignore the provided [newValueCompute]. The up-to-date check is made using [CachedValue] container,
     * which holds the [Instant] which provides the time that an operation requested to update the cache (not the time the
     * update takes place). However this check do not guarantee that the cached value will be up-to-date, as we cannot guarantee
     * that the time the operation was requested matches the time the data was created.
     */
    fun updateCache(keys: List<K>, newValueCompute: (K) -> V) {
        this.cache.updateCache(keys, newValueCompute)
    }
}