| 956 | } |
| 957 | |
| 958 | bool DataCache::Partition::Store(const CacheKey& cache_key, const uint8_t* buffer, |
| 959 | int64_t buffer_len, bool* start_reclaim) { |
| 960 | DCHECK(!closed_); |
| 961 | *start_reclaim = false; |
| 962 | Slice key = cache_key.ToSlice(); |
| 963 | const int64_t charge_len = BitUtil::RoundUp(buffer_len, PAGE_SIZE); |
| 964 | if (charge_len > capacity_) return false; |
| 965 | |
| 966 | // Check for existing entry. |
| 967 | { |
| 968 | Cache::UniqueHandle handle(meta_cache_->Lookup(key, Cache::NO_UPDATE)); |
| 969 | if (handle.get() != nullptr) { |
| 970 | if (HandleExistingEntry(key, handle, buffer, buffer_len)) return false; |
| 971 | } |
| 972 | } |
| 973 | |
| 974 | CacheFile* cache_file; |
| 975 | int64_t insertion_offset; |
| 976 | if (LIKELY(!trace_replay_)) { |
| 977 | std::unique_lock<SpinLock> partition_lock(lock_); |
| 978 | |
| 979 | // Limit the write concurrency to avoid blocking the caller (which could be calling |
| 980 | // from the critical path of an IO read) when the cache becomes IO bound due to either |
| 981 | // limited memory for page cache or the cache is undersized which leads to eviction. |
| 982 | const bool exceed_concurrency = |
| 983 | pending_insert_set_.size() >= data_cache_write_concurrency_; |
| 984 | if (exceed_concurrency || |
| 985 | pending_insert_set_.find(key.ToString()) != pending_insert_set_.end()) { |
| 986 | ImpaladMetrics::IO_MGR_REMOTE_DATA_CACHE_DROPPED_BYTES->Increment(buffer_len); |
| 987 | ImpaladMetrics::IO_MGR_REMOTE_DATA_CACHE_DROPPED_ENTRIES->Increment(1); |
| 988 | Trace(trace::EventType::STORE_FAILED_BUSY, cache_key, /*lookup_len=*/-1, |
| 989 | buffer_len); |
| 990 | return false; |
| 991 | } |
| 992 | |
| 993 | // Allocate from the backing file. |
| 994 | CHECK(!cache_files_.empty()); |
| 995 | cache_file = cache_files_.back().get(); |
| 996 | insertion_offset = cache_file->Allocate(charge_len, partition_lock); |
| 997 | // Create and append to a new file if necessary. |
| 998 | if (UNLIKELY(insertion_offset < 0)) { |
| 999 | if (!CreateCacheFile().ok()) return false; |
| 1000 | cache_file = cache_files_.back().get(); |
| 1001 | insertion_offset = cache_file->Allocate(charge_len, partition_lock); |
| 1002 | if (UNLIKELY(insertion_offset < 0)) return false; |
| 1003 | } |
| 1004 | |
| 1005 | // Start deleting old files if there are too many opened. |
| 1006 | *start_reclaim = cache_files_.size() > max_opened_files_; |
| 1007 | |
| 1008 | // Do this last. At this point, we are committed to inserting 'key' into the cache. |
| 1009 | pending_insert_set_.emplace(key.ToString()); |
| 1010 | } else { |
| 1011 | DCHECK(buffer == nullptr); |
| 1012 | cache_file = nullptr; |
| 1013 | insertion_offset = 0; |
| 1014 | } |
| 1015 | |