Class StorageLocation

java.lang.Object
org.apache.druid.segment.loading.StorageLocation

@ThreadSafe public class StorageLocation extends Object
Logical representation of a local disk path to store CacheEntry, controlling that the total size of stored files doesn't exceed the maxSizeBytes and available space is always kept smaller than freeSpaceToKeep.

CacheEntry can be stored in two manners in a storage location. The first is to store the entry indefinitely with reserve(CacheEntry), where the space of the entry is accounted for and the storage space will not be recovered until release(CacheEntry) is called. These entries are stored in staticCacheEntries.

The second way is to store as a transient cache item with one of reserveWeak(CacheEntry), addWeakReservationHold(CacheEntryIdentifier, Supplier), or addWeakReservationHoldIfExists(CacheEntryIdentifier). CacheEntry stored in this manner will exist on disk in this location until the point that another new reservation needs more space than remains available in the location, at which point reclaim(long) will be called to try to call CacheEntry.unmount() on any eligible entries until enough space is available to store the new item.

Items are chosen for eviction using an algorithm based on SIEVE with additional mechanisms to place temporary holds on cache entries. The holds are required to support cases such as if a group of cache entries are taking part in a query and must all be loaded simultaneously before processing can continue.

Implementation-wise, CacheEntry are wrapped in StorageLocation.WeakCacheEntry to form a doubly linked list functioning as a queue which can be interacted with via 3 fields, head, tail, and hand. Head and tail expectedly mark the beginning and end of the queue, while the hand is the interesting bit and is used as the position of the current item to consider for eviction the next time reclaim(long) needs to be called. Entries are also stored in a map, weakCacheEntries, for fast retrieval. Using a weak cache entry sets StorageLocation.WeakCacheEntry.visited to true, and addWeakReservationHold(CacheEntryIdentifier, Supplier) and addWeakReservationHoldIfExists(CacheEntryIdentifier) additionally will call StorageLocation.WeakCacheEntry.hold() until StorageLocation.ReservationHold.close() is called which will call StorageLocation.WeakCacheEntry.release().

When it is time to reclaim space, first the hand is checked for holds - if any exist then the hand is moved to StorageLocation.WeakCacheEntry.prev immediately and we try again. If no holds are present, then it is checked if it has been marked as StorageLocation.WeakCacheEntry.visited - if so then it is unmarked as visited, and the hand moves to StorageLocation.WeakCacheEntry.prev (allowing this entry to be reclaimed the next time we pass if it has not been visited again). Lastly, if neither under a hold or marked, the entry will be unlinked from the queue AND unmounted from the storage location (deleting the files from disk) with unlinkWeakEntry(WeakCacheEntry). This process is repeated until either a sufficient amount of space has been reclaimed, or no additional space is able to be reclaimed, in which case the new reservation fails.

There is an auxilary mode for weak references when areWeakEntriesEphemeral is set to true. In this mode, weak entries are short-lived entries that only exist while one or more reservation hold are active, and are unmounted when the holds are released. This is useful in cases where entries are unlikely to be re-used such as in asynchronous tasks.

This class is thread-safe, so that multiple threads can update its state at the same time. One example usage is that a historical server can use multiple threads to load different segments in parallel from deep storage.