| /** @file |
| |
| Preservation of documents that would be overwritten by the write head. |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include "AggregateWriteBuffer.h" |
| #include "iocore/cache/CacheDefs.h" |
| #include "P_CacheDir.h" |
| #include "Stripe.h" |
| |
| #include "iocore/eventsystem/EThread.h" |
| #include "iocore/eventsystem/ProxyAllocator.h" |
| |
| #include "tscore/Allocator.h" |
| #include "tscore/CryptoHash.h" |
| #include "tscore/ink_platform.h" |
| #include "tscore/List.h" |
| |
| #define EVACUATION_SIZE (2 * AGG_SIZE) // 8MB |
| #define EVACUATION_BUCKET_SIZE (2 * EVACUATION_SIZE) // 16MB |
| #define PIN_SCAN_EVERY 16 // scan every 1/16 of disk |
| |
| #define dir_offset_evac_bucket(_o) (_o / (EVACUATION_BUCKET_SIZE / CACHE_BLOCK_SIZE)) |
| #define dir_evac_bucket(_e) dir_offset_evac_bucket(dir_offset(_e)) |
| #define offset_evac_bucket(_d, _o) \ |
| dir_offset_evac_bucket((_d->offset_to_vol_offset(_o) |
| |
| class CacheEvacuateDocVC; |
| |
| // Key and Earliest key for each fragment that needs to be evacuated |
| struct EvacuationKey { |
| SLink<EvacuationKey> link; |
| CryptoHash key; |
| CryptoHash earliest_key; |
| }; |
| |
| struct EvacuationBlock { |
| union { |
| unsigned int init; |
| struct { |
| unsigned int done : 1; // has been evacuated |
| unsigned int pinned : 1; // check pinning timeout |
| unsigned int evacuate_head : 1; // check pinning timeout |
| unsigned int unused : 29; |
| } f; |
| }; |
| |
| int readers; |
| Dir dir; |
| Dir new_dir; |
| // we need to have a list of evacuationkeys because of collision. |
| EvacuationKey evac_frags; |
| CacheEvacuateDocVC *earliest_evacuator; |
| LINK(EvacuationBlock, link); |
| }; |
| |
| /** |
| * Represents the collection of documents that must be rewritten to the cache |
| * to avoid being overwritten. The documents themselves are not owned by this |
| * table, but are referenced by it via a cache directory entry. If any |
| * directory entry stored in this table is invalidated, this table is also |
| * invalidated. Once a document has been rewritten, mark its block as done |
| * and it will be removed on the next call to periodic_scan. |
| * |
| * This class is not safe for concurrent access. It should be protected |
| * by a lock. |
| * |
| * Destructing this object without first releasing all blocks and calling |
| * periodic_scan may result in memory leaks. |
| * |
| * @see Stripe |
| */ |
| class PreservationTable |
| { |
| public: |
| int evacuate_size{}; |
| |
| /** |
| * The table of preserved documents. |
| * |
| * This is implemented as a hash table using separate chaining. |
| */ |
| DLL<EvacuationBlock> *evacuate{nullptr}; |
| |
| /** |
| * PreservationTable constructor |
| * |
| * This will abort the program if it is unable to allocate memory. |
| * |
| * @param size The total number of directory entries the table must |
| * be able to store. The size must be a positive number. |
| */ |
| PreservationTable(off_t size); |
| |
| ~PreservationTable(); |
| |
| PreservationTable(PreservationTable const &that) = delete; |
| PreservationTable &operator=(PreservationTable const &that) = delete; |
| |
| PreservationTable(PreservationTable &&that); |
| PreservationTable &operator=(PreservationTable &&that); |
| |
| /** |
| * Check whether the hash table may be indexed with the given offset. |
| * |
| * @param bucket An index into the hash table. |
| * @return Returns true if the index is valid, false otherwise. |
| */ |
| bool evac_bucket_valid(off_t bucket) const; |
| |
| /** |
| * Force the preservation of the given document. |
| * |
| * @param dir The directory entry for the document to preserve. |
| * @param pinned Whether the document is pinned (0 or 1). |
| */ |
| void force_evacuate_head(Dir const *evac_dir, int pinned); |
| |
| /** |
| * Find the evacuation block corresponding to @a dir. |
| * |
| * @param dir The directory entry to search for. |
| * @return Returns the corresponding block if it exists, nullptr otherwise. |
| */ |
| EvacuationBlock *find(Dir const &dir) const; |
| |
| /** |
| * Acquire the evacuation block for @a dir. |
| * |
| * Any number of readers may acquire the block at a time to prevent the |
| * block from being removed from the table. If no block for the directory |
| * entry is in the table yet, one will be added with @a key. |
| * |
| * @param dir The directory entry to acquire. |
| * @param key The key for the directory entry. |
| * @return Returns 1 if a new block was created, otherwise 0. |
| */ |
| int acquire(Dir const &dir, CacheKey const &key); |
| |
| /** Release the evacuation block for @a dir. |
| * |
| * When a block has been released once for every time it was acquired, it |
| * may be removed from the table, invalidating all pointers to it. Note that |
| * releasing more than once from the same reader may cause the block to be |
| * removed from the table while other readers that acquired it think it's |
| * valid. Be careful. |
| * |
| * A block that was evacuated with force_evacuate_head will not be removed |
| * from the table when it is released. |
| * |
| * @param dir The directory entry to release. |
| * @see force_evacuate_head |
| */ |
| void release(Dir const &dir); |
| |
| /** |
| * Remove completed documents from the table and add pinned documents. |
| * |
| * Documents that were acquired by a reader and not released are not removed. |
| * Invalidates pointers to evacuation blocks unless they have been acquired. |
| * |
| * @param Stripe The stripe to scan for pinned documents to preserve. |
| */ |
| void periodic_scan(Stripe *stripe); |
| |
| private: |
| void cleanup(Stripe const *stripe); |
| void remove_finished_blocks(Stripe const *stripe, int bucket); |
| void scan_for_pinned_documents(Stripe const *stripe); |
| |
| EvacuationBlock *find(Dir const &dir, int bucket) const; |
| }; |
| |
| inline bool |
| PreservationTable::evac_bucket_valid(off_t bucket) const |
| { |
| return (bucket >= 0 && bucket < evacuate_size); |
| } |
| |
| extern ClassAllocator<EvacuationBlock, false> evacuationBlockAllocator; |
| extern ClassAllocator<EvacuationKey, false> evacuationKeyAllocator; |
| |
| inline EvacuationBlock * |
| new_EvacuationBlock() |
| { |
| EvacuationBlock *b = THREAD_ALLOC(evacuationBlockAllocator, this_ethread()); |
| b->init = 0; |
| b->readers = 0; |
| b->earliest_evacuator = nullptr; |
| b->evac_frags.link.next = nullptr; |
| return b; |
| } |
| |
| inline void |
| free_EvacuationBlock(EvacuationBlock *b) |
| { |
| EvacuationKey *e = b->evac_frags.link.next; |
| while (e) { |
| EvacuationKey *n = e->link.next; |
| evacuationKeyAllocator.free(e); |
| e = n; |
| } |
| THREAD_FREE(b, evacuationBlockAllocator, this_ethread()); |
| } |