libfoedus-core
FOEDUS Core Library
cache_manager_pimpl.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2015, Hewlett-Packard Development Company, LP.
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details. You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * HP designates this particular file as subject to the "Classpath" exception
16  * as provided by HP in the LICENSE.txt file that accompanied this code.
17  */
19 
20 #include <glog/logging.h>
21 
22 #include <chrono>
23 #include <sstream>
24 #include <string>
25 
26 #include "foedus/engine.hpp"
34 
35 namespace foedus {
36 namespace cache {
37 
39  : engine_(engine),
40  stop_requested_(false),
41  pool_(nullptr),
42  hashtable_(nullptr),
43  reclaimed_pages_(nullptr),
44  reclaimed_pages_count_(0) {
45 }
46 
48  if (engine_->is_master()) {
49  // nothing to do in master engine
50  return kRetOk;
51  } else if (!engine_->get_memory_manager()->is_initialized()) {
53  }
54 
55  LOG(INFO) << "Initializing Snapshot Cache in Node-" << engine_->get_soc_id() << "...";
56 
58  pool_ = node->get_snapshot_pool();
61 
62  // So far, the reclaimed_pages_memory_ is large enough to hold all pages in the pool at once.
63  // This is obviously too much for most cases, but wouldn't be an issue as the memory consumption
64  // is anyway negligibly smaller than the page pool itself. Keep it simple stupid.
66  const CacheOptions& options = engine_->get_options().cache_;
67 
70 
71  // exclude page counts initially grabbed by each thread.
72  uint64_t initial_allocations
75  cleaner_threshold_ += initial_allocations;
76  urgent_threshold_ += initial_allocations;
79  }
82  }
83 
87 
90  1ULL << 21,
92  engine_->get_soc_id());
94 
95  // launch the cleaner thread
96  stop_requested_.store(false);
97  cleaner_ = std::move(std::thread(&CacheManagerPimpl::handle_cleaner, this));
98 
99  return kRetOk;
100 }
101 
103  if (engine_->is_master()) {
104  // nothing to do in master engine
105  return kRetOk;
106  }
107 
108  LOG(INFO) << "Uninitializing Snapshot Cache... " << describe();
110 
111  pool_ = nullptr;
112  hashtable_ = nullptr;
113  reclaimed_pages_ = nullptr;
116  return kRetOk;
117 }
118 
120  LOG(INFO) << "Here we go. Cleaner thread: " << describe();
121 
122  const uint32_t kIntervalMs = 5; // should be a bit shorter than epoch-advance interval
123  while (!stop_requested_) {
124  DVLOG(2) << "Cleaner thread came in: " << describe();
130  VLOG(0) << "Time to evict: " << describe();
131 
132  uint64_t target_count = stat.allocated_pages_ - cleaner_threshold_;
133  debugging::StopWatch evict_watch;
134  handle_cleaner_evict_pages(target_count);
135  evict_watch.stop();
136  VLOG(0) << "Evicted " << reclaimed_pages_count_ << " pages in " << evict_watch.elapsed_us()
137  << "us: " << describe();
138 
139  if (reclaimed_pages_count_ > 0) {
140  // We collected some number of pages in previous execution. now we have to wait for
141  // grace period before returning them to the pool as free pages.
142  xct::XctManager* xct_manager = engine_->get_xct_manager();
143  ASSERT_ND(xct_manager->is_initialized()); // see stop_cleaner()'s comment why this is true.
144  Epoch reclaimed_pages_epoch = xct_manager->get_current_global_epoch();
145  VLOG(0) << "Collected " << reclaimed_pages_count_ << " pages. let's return them to "
146  << "the pool: " << describe();
147  Epoch wait_until = reclaimed_pages_epoch.one_more();
148 
149  // We have to wait for grace-period, in other words until the next epoch.
150  if (stat.allocated_pages_ >= urgent_threshold_) {
151  VLOG(0) << "We are in urgent lack of free pages, let's advance epoch right now";
152  xct_manager->advance_current_global_epoch();
153  }
154  // wait forever, but occasionally wake up to check if the system is being shutdown
155  while (!stop_requested_ && xct_manager->get_current_global_epoch() < wait_until) {
156  const uint64_t interval_microsec = 100000ULL;
157  xct_manager->wait_for_current_global_epoch(wait_until, interval_microsec);
158  }
159 
160  if (!stop_requested_) {
161  Epoch current_epoch = xct_manager->get_current_global_epoch();
162  ASSERT_ND(reclaimed_pages_epoch < current_epoch);
163  VLOG(0) << "Okay! reclaimed_pages_epoch_=" << reclaimed_pages_epoch
164  << ", current_epoch=" << current_epoch;
165 
169  }
170  } else {
171  LOG(INFO) << "Wtf, we couldn't collect any pages? that's weird...: " << describe();
172  }
173 
174  if (stat.allocated_pages_ >= urgent_threshold_) {
175  LOG(INFO) << "Umm, still severely lacking free pages. This might mean"
176  " that the cleaner thread is getting behind or the pool is too small: " << describe();
177  }
178  } else {
179  DVLOG(2) << "Still enough free pages. do nothing";
180  }
181 
182  if (!stop_requested_) {
183  std::this_thread::sleep_for(std::chrono::milliseconds(kIntervalMs));
184  }
185  }
186 
187  LOG(INFO) << "Quiting... Cleaner thread: " << describe();
188 }
189 
192  ASSERT_ND(target_count > 0);
193  CacheHashtable::EvictArgs args = { target_count, 0, reclaimed_pages_ };
194  hashtable_->evict(&args);
196 }
197 
199  bool original = false;
200  bool changed = stop_requested_.compare_exchange_strong(original, true);
201  if (changed) {
202  if (cleaner_.joinable()) {
203  LOG(INFO) << "Requesting Cache Cleaner to stop...";
204  cleaner_.join();
205  }
206  } else {
207  LOG(INFO) << "Cache Cleaner seems already stop-requested";
208  }
209  return kRetOk;
210 }
211 
212 
213 std::string CacheManagerPimpl::describe() const {
214  if (pool_ == nullptr) {
215  return "<SnapshotCacheManager />";
216  }
217 
218  std::stringstream str;
219  memory::PagePool::Stat pool_stat = pool_->get_stat();
220  str << "<SpCache "
221  << " node=\"" << engine_->get_soc_id() << "\""
222  << " total=\"" << pool_stat.total_pages_ << "\""
223  << " alloc=\"" << pool_stat.allocated_pages_ << "\""
224  << " threshold=\"" << cleaner_threshold_ << "\""
225  << " urgent_threshold=\"" << urgent_threshold_ << "\""
226  << " reclaimed_count=\"" << reclaimed_pages_count_ << "\""
227  << ">" << reclaimed_pages_memory_ << "</SpCache>";
228  return str.str();
229 }
230 
231 
232 
233 } // namespace cache
234 } // namespace foedus
numa_alloc_onnode() and numa_free().
memory::PagePoolOffset * reclaimed_pages_
This buffers pages being reclaimed.
#define ERROR_STACK(e)
Instantiates ErrorStack with the given foedus::error_code, creating an error stack with the current f...
uint32_t private_page_pool_initial_grab_
How many pages each NumaCoreMemory initially grabs when it is initialized.
Epoch get_current_global_epoch() const
Returns the current global epoch, the epoch a newly started transaction will be in.
void release_block()
Releases the memory block.
CacheHashtable * hashtable_
The cache hashtable (SnapshotPageId -> offset in pool) on top of the page pool.
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
Stat get_stat() const
Definition: page_pool.cpp:118
bool is_initialized() const override
Returns whether the object has been already initialized or not.
Definition: xct_manager.cpp:31
uint32_t PagePoolOffset
Offset in PagePool that compactly represents the page address (unlike 8 bytes pointer).
Definition: memory_id.hpp:44
0x0005 : "GENERAL: A dependent module is not initialized yet. This implies a wrong initialization ord...
Definition: error_code.hpp:109
Brings error stacktrace information as return value of functions.
Definition: error_stack.hpp:81
Represents a time epoch.
Definition: epoch.hpp:61
void alloc(uint64_t size, uint64_t alignment, AllocType alloc_type, int numa_node) noexcept
Allocate a memory, releasing the current memory if exists.
std::atomic< bool > stop_requested_
to stop cleaner_
uint64_t urgent_threshold_
the number of allocated pages above which cleaner advances epoch to release pages ...
memory::PagePool * pool_
The SOC-local snapshot page pool in this SOC engine.
const EngineOptions & get_options() const
Definition: engine.cpp:39
bool is_master() const
Returns if this engine object is a master instance.
Definition: engine.cpp:68
uint64_t evicted_count_
[Out] Number of entries that were actually evicted
memory::AlignedMemory reclaimed_pages_memory_
The memory backing reclaimed_pages_.
void handle_cleaner_evict_pages(uint64_t target_count)
Evicts pages up to about target_count (maybe a bit more or less).
memory::MemoryOptions memory_
void evict(EvictArgs *args)
Evict some entries from the hashtable.
std::thread cleaner_
The only cleaner thread in this SOC engine.
Database engine object that holds all resources and provides APIs.
Definition: engine.hpp:109
float snapshot_cache_urgent_threshold_
When the cache eviction performs in an urgent mode, which immediately advances the current epoch to r...
Repository of memories dynamically acquired and shared within one NUMA node (socket).
uint64_t stop()
Take another current time tick.
Definition: stop_watch.cpp:35
float snapshot_cache_eviction_threshold_
When to start evicting pages in fraction of snapshot page pool capacity.
Epoch one_more() const
Definition: epoch.hpp:127
Xct Manager class that provides API to begin/abort/commit transaction.
Definition: xct_manager.hpp:35
Set of options for snapshot cache manager.
void * get_block() const
Returns the memory block.
void release(uint32_t desired_release_count, PagePoolOffsetChunk *chunk)
Returns the specified number of free pages from the chunk.
Definition: page_pool.cpp:134
Used to point to an already existing array.
Definition: page_pool.hpp:81
xct::XctManager * get_xct_manager() const
See Transaction Manager.
Definition: engine.cpp:61
double elapsed_us() const
Definition: stop_watch.hpp:45
cache::CacheHashtable * get_snapshot_cache_table()
void wait_for_current_global_epoch(Epoch target_epoch, int64_t wait_microseconds=-1)
Passively wait until the current global epoch becomes the given value.
Definition: xct_manager.cpp:35
thread::ThreadOptions thread_
#define CHECK_ERROR(x)
This macro calls x and checks its returned value.
NumaNodeMemory * get_local_memory() const
const ErrorStack kRetOk
Normal return value for no-error case.
void advance_current_global_epoch()
Requests to advance the current global epoch as soon as possible and blocks until it actually does...
uint64_t reclaimed_pages_count_
Number of pages buffered so far.
void handle_cleaner()
Main routine of cleaner_.
uint64_t cleaner_threshold_
the number of allocated pages above which cleaner starts cleaning
ThreadId get_total_thread_count() const
void memory_fence_acquire()
Equivalent to std::atomic_thread_fence(std::memory_order_acquire).
soc::SocId get_soc_id() const
If this is a child instance, returns its SOC ID (NUMA node).
Definition: engine.cpp:73
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72
A high-resolution stop watch.
Definition: stop_watch.hpp:30
memory::EngineMemory * get_memory_manager() const
See Memory Manager.
Definition: engine.cpp:50
bool is_initialized() const override final
Returns whether the object has been already initialized or not.
cache::CacheOptions cache_