libfoedus-core
FOEDUS Core Library
foedus::log::ThreadLogBufferMeta Struct Referencefinal

Metadata part of ThreadLogBuffer, without the actual buffer which is way way larger. More...

Detailed Description

Metadata part of ThreadLogBuffer, without the actual buffer which is way way larger.

Circular Log Buffer
This class forms a circular buffer used by log appender (Thread, which we call "the thread" or "this thread" all over the place), log writer (Logger), and log gleaner (LogGleaner). We maintain four offsets on the buffer.
MarkerRead byWritten byDescription
offset_head_ (H) ThreadThread, LogGleaner This marks the position where log entries start. This private log buffer is a circular buffer where the head is eaten by log gleaner. However, log gleaner is okay to get behind, reading from log file instead (but slower). Thus, offset_head_ is advanced either by log gleaner or this thread. If the latter happens, log gleaner has to give up using in-memory logs and instead read from log files.
offset_durable_ (D) Thread, LogGleanerLogger This marks the position upto which the log writer durably wrote out to log files. Everything after this position must not be discarded because they are not yet durable. When the log writer reads log entries after here, writes them to log file, and calls fsync, this variable is advanced by the log writer. This variable is read by this thread to check the end of the circular buffer.
offset_committed_ (C) Thread, LoggerThread This marks the position upto which transaction logs are committed by the thread. Log writers can safely read log entries and write them to log files up to this place. When the transaction commits, this value is advanced by the thread. The only possible update pattern to this variable is advance by this thread. Thus, the log writer can safely read this variable without any fence or lock thanks to regular (either old value or new value, never garbage) read of 64-bit.
offset_tail_ (T) ThreadThread The current cursor to which next log will be written. This is the location the current transaction of this thread is writing to before commit. When the transaction commits, offset_committed_ catches up with this. When the transaction aborts, this value rolls back to offset_committed_. Only this thread reads/writes to this variable. No other threads access this.
Circular Epoch Mark array
This class also forms a circulary array of ThreadEpockMark to mark where the thread switched epochs to help the logger. oldest_mark_index_ (inclusive) to current_mark_index_ (exclusive) are the loggable entries that can be written out by the logger. current_mark_index_ is now being written by the thread, so the logger avoids writing logs in that epoch (it's doable to write out logs in this epoch, but we avoid it to simplify and also maximize the batch-size of I/O in the logger). Further, the following section details one special case.
Epoch Mark while the thread is idle
This is complicated, thus worth a detailed explanation. As described in the xct manager, we use the in-commit epoch guard to advance epochs even when some thread is idle and cannot advance the thread's latest_epoch. And, the logger write out logs up to current global epoch - 2, so this class is guaranteed to not have any real race (eg the thread now writing a new log entry or a new epoch mark for the written_epoch). Hence, get_logs_to_write() and on_log_written() handle the following situations:
  • Case 1) The oldest mark has new_epoch>written_epoch : This means the thread is going faster than the logger, and there is no log in the written_epoch. Empty log range by get_logs_to_write() and nothing to do in on_log_written().
  • Case 2) The oldest mark has new_epoch==written_epoch : The mark is exactly the one the logger is looking for, but there are two cases.
  • Case 2-a) The oldest mark is not current mark (oldest!=current) : Quite usual. Valid log range by get_logs_to_write() and on_log_written() increments oldest_mark_index_.
  • Case 2-b) The oldest mark is the current mark (oldest==current) : This is the case where the thread is idle for a while after writing a log in written_epoch. Because oldest mark is the current mark, offset_end_ is not populated yet. However, because the thread never emits new logs in written_epoch, the logger can safely populate it with offset_committed_ (with a few fences). on_log_written() does nothing in this case.
  • Case 3) The oldest mark has new_epoch<written_epoch : This happens in the next logger visit after 2-b. If still oldest==current, empty log range and nothing to do in on_log_written(). If oldest!=current, we advance oldest as much as possible to determine log range. Don't worry about synchronization here. Again, the thread emits log only in current or current-1 epoch, so there is no chance it adds a new epoch mark with new_epoch<=writen_epoch.
See also
foedus::log::ThreadEpockMark
foedus::log::ThreadLogBuffer

Definition at line 147 of file thread_log_buffer.hpp.

#include <thread_log_buffer.hpp>

Collaboration diagram for foedus::log::ThreadLogBufferMeta:

Public Types

enum  Constants { kMaxNonDurableEpochs = 64 }
 

Public Member Functions

 ThreadLogBufferMeta ()
 
void assert_consistent () const
 Only for Debug-assertion. More...
 

Static Public Member Functions

static uint32_t increment_mark_index (uint32_t index)
 

Public Attributes

thread::ThreadId thread_id_
 
uint16_t padding1_
 
uint32_t padding2_
 
uint64_t buffer_size_
 Size of the buffer assigned to this thread. More...
 
uint64_t buffer_size_safe_
 buffer_size_ - 64. More...
 
uint64_t offset_head_
 This marks the position where log entries start. More...
 
uint64_t offset_durable_
 This marks the position upto which the log writer durably wrote out to log files. More...
 
uint64_t offset_committed_
 This marks the position upto which transaction logs are committed by the thread. More...
 
uint64_t offset_tail_
 The current cursor to which next log will be written. More...
 
ThreadEpockMark thread_epoch_marks_ [kMaxNonDurableEpochs]
 Circular array of epoch marks of a thread log buffer. More...
 
uint32_t oldest_mark_index_
 Array index in thread_epoch_marks_ that indicates the oldest epoch mark to be consumed by the logger. More...
 
uint32_t current_mark_index_
 Array index in thread_epoch_marks_ that indicates the current epoch being appended by the thread, so the logger must avoid touching the epoch. More...
 

Friends

std::ostream & operator<< (std::ostream &o, const ThreadLogBufferMeta &v)
 

Member Enumeration Documentation

Enumerator
kMaxNonDurableEpochs 

Each thread can have at most this number of epochs in this log buffer.

For example, if the assigned logger made the logs of this thread durable up to ep-3, the thread must not write out further logs in ep-67. It has to wait until the logger catches up. As far as the user assigns a sufficient number of loggers, it shouldn't cause any issue.

Definition at line 148 of file thread_log_buffer.hpp.

148  {
158  };
Each thread can have at most this number of epochs in this log buffer.

Constructor & Destructor Documentation

foedus::log::ThreadLogBufferMeta::ThreadLogBufferMeta ( )

Definition at line 44 of file thread_log_buffer.cpp.

References buffer_size_, buffer_size_safe_, current_mark_index_, offset_committed_, offset_durable_, offset_head_, offset_tail_, oldest_mark_index_, thread_epoch_marks_, and thread_id_.

44  {
45  thread_id_ = 0;
46  buffer_size_ = 0;
48 
49  offset_head_ = 0;
50  offset_durable_ = 0;
52  offset_tail_ = 0;
53 
54  std::memset(thread_epoch_marks_, 0, sizeof(thread_epoch_marks_));
57 }
uint32_t current_mark_index_
Array index in thread_epoch_marks_ that indicates the current epoch being appended by the thread...
uint64_t buffer_size_
Size of the buffer assigned to this thread.
uint64_t offset_committed_
This marks the position upto which transaction logs are committed by the thread.
ThreadEpockMark thread_epoch_marks_[kMaxNonDurableEpochs]
Circular array of epoch marks of a thread log buffer.
uint64_t offset_head_
This marks the position where log entries start.
uint64_t offset_durable_
This marks the position upto which the log writer durably wrote out to log files. ...
uint64_t offset_tail_
The current cursor to which next log will be written.
uint32_t oldest_mark_index_
Array index in thread_epoch_marks_ that indicates the oldest epoch mark to be consumed by the logger...
uint64_t buffer_size_safe_
buffer_size_ - 64.

Member Function Documentation

void foedus::log::ThreadLogBufferMeta::assert_consistent ( ) const

Only for Debug-assertion.

This method must be called by the worker thread itself. When logger calls it, it must make sure the worker is not modifying anything concurrently.

Definition at line 59 of file thread_log_buffer.cpp.

References ASSERT_ND, buffer_size_, current_mark_index_, increment_mark_index(), foedus::Epoch::is_valid(), kMaxNonDurableEpochs, foedus::log::ThreadEpockMark::new_epoch_, foedus::log::ThreadEpockMark::offset_begin_, offset_committed_, offset_durable_, foedus::log::ThreadEpockMark::offset_end_, offset_head_, offset_tail_, foedus::log::ThreadEpockMark::old_epoch_, oldest_mark_index_, and thread_epoch_marks_.

Referenced by foedus::log::ThreadLogBuffer::assert_consistent().

59  {
60 #ifndef NDEBUG
61  // These assertions might have a false positive unless run in a 1-threaded situation.
62  // But, it would be negligibly rare, and anyway it is wiped out in release mode.
63  // So, if you believe you are hitting assertion here because of a sheer luck, ignore.
64  // In 99% cases these assersions fire for a valid reason, though.
65  // Q: Making it thread-safe? A: That means even non-debug codepath becomes superslow.
66  // Note, we thus use this method only from the worker thread itself. Then it's safe.
71  // because of wrap around, *at most* one of them does not hold
72  int violation_count = 0;
74  ++violation_count;
75  }
77  ++violation_count;
78  }
80  ++violation_count;
81  }
82  ASSERT_ND(violation_count <= 1);
83  ASSERT_ND(oldest_mark_index_ < static_cast<uint32_t>(kMaxNonDurableEpochs));
84  ASSERT_ND(current_mark_index_ < static_cast<uint32_t>(kMaxNonDurableEpochs));
85 
86  for (uint32_t i = oldest_mark_index_; i != current_mark_index_; i = increment_mark_index(i)) {
87  ThreadEpockMark mark = thread_epoch_marks_[i];
88  ThreadEpockMark next = thread_epoch_marks_[increment_mark_index(i)];
89  ASSERT_ND(mark.old_epoch_.is_valid());
90  ASSERT_ND(mark.new_epoch_.is_valid());
91  ASSERT_ND(mark.old_epoch_ < mark.new_epoch_);
92  ASSERT_ND(mark.new_epoch_ == next.old_epoch_);
93  ASSERT_ND(mark.offset_end_ == next.offset_begin_);
94  }
95  ThreadEpockMark cur = thread_epoch_marks_[current_mark_index_];
96  ASSERT_ND(cur.old_epoch_.is_valid());
97  ASSERT_ND(cur.new_epoch_.is_valid());
98  ASSERT_ND(cur.old_epoch_ < cur.new_epoch_);
99  ASSERT_ND(cur.offset_end_ == 0 || cur.offset_end_ == offset_committed_);
100 #endif // NDEBUG
101 }
static uint32_t increment_mark_index(uint32_t index)
uint32_t current_mark_index_
Array index in thread_epoch_marks_ that indicates the current epoch being appended by the thread...
uint64_t buffer_size_
Size of the buffer assigned to this thread.
Each thread can have at most this number of epochs in this log buffer.
uint64_t offset_committed_
This marks the position upto which transaction logs are committed by the thread.
ThreadEpockMark thread_epoch_marks_[kMaxNonDurableEpochs]
Circular array of epoch marks of a thread log buffer.
uint64_t offset_head_
This marks the position where log entries start.
uint64_t offset_durable_
This marks the position upto which the log writer durably wrote out to log files. ...
uint64_t offset_tail_
The current cursor to which next log will be written.
uint32_t oldest_mark_index_
Array index in thread_epoch_marks_ that indicates the oldest epoch mark to be consumed by the logger...
#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

Here is the call graph for this function:

Here is the caller graph for this function:

static uint32_t foedus::log::ThreadLogBufferMeta::increment_mark_index ( uint32_t  index)
inlinestatic

Definition at line 170 of file thread_log_buffer.hpp.

References kMaxNonDurableEpochs.

Referenced by assert_consistent(), foedus::log::ThreadLogBuffer::get_logs_to_write(), foedus::log::ThreadLogBuffer::on_log_written(), and foedus::log::operator<<().

170  {
171  return (index + 1) % kMaxNonDurableEpochs;
172  }
Each thread can have at most this number of epochs in this log buffer.

Here is the caller graph for this function:

Friends And Related Function Documentation

std::ostream& operator<< ( std::ostream &  o,
const ThreadLogBufferMeta v 
)
friend

Definition at line 335 of file thread_log_buffer.cpp.

335  {
336  o << "<ThreadLogBuffer>";
337  o << "<thread_id_>" << v.thread_id_ << "</thread_id_>";
338  o << "<buffer_size_>" << v.buffer_size_ << "</buffer_size_>";
339  o << "<offset_head_>" << v.offset_head_ << "</offset_head_>";
340  o << "<offset_durable_>" << v.offset_durable_ << "</offset_durable_>";
341  o << "<offset_committed_>" << v.offset_committed_ << "</offset_committed_>";
342  o << "<offset_tail_>" << v.offset_tail_ << "</offset_tail_>";
343  o << "<thread_epoch_marks"
344  << " oldest=\"" << v.oldest_mark_index_ << "\""
345  << " current=\"" << v.current_mark_index_ << "\""
346  << ">";
347 
348  // from oldest (inclusive) to current (exclusive).
349  for (uint32_t i = v.oldest_mark_index_;
350  i != v.current_mark_index_;
352  o << "<Entry index=\"" << i << "\">" << v.thread_epoch_marks_[i] << "</Entry>";
353  }
354  // also write out current entry
355  o << "<Entry index=\"" << v.current_mark_index_ << "\">"
356  << v.thread_epoch_marks_[v.current_mark_index_]
357  << "</Entry>";
358  o << "</thread_epoch_marks>";
359  o << "</ThreadLogBuffer>";
360  return o;
361 }
static uint32_t increment_mark_index(uint32_t index)

Member Data Documentation

uint64_t foedus::log::ThreadLogBufferMeta::buffer_size_
uint64_t foedus::log::ThreadLogBufferMeta::buffer_size_safe_

buffer_size_ - 64.

We always leave some hole between offset_tail_ and offset_head_ to avoid the case offset_tail_ == offset_head_ (log empty? or log full?). One classic way to handle this case is to store count rather than offsets, but it makes synchronization between log writer and this thread expensive. Rather, we sacrifice a negligible space.

Definition at line 189 of file thread_log_buffer.hpp.

Referenced by foedus::log::ThreadLogBuffer::initialize_once(), foedus::log::ThreadLogBuffer::reserve_new_log(), ThreadLogBufferMeta(), and foedus::log::ThreadLogBuffer::wait_for_space().

uint32_t foedus::log::ThreadLogBufferMeta::current_mark_index_

Array index in thread_epoch_marks_ that indicates the current epoch being appended by the thread, so the logger must avoid touching the epoch.

Invariant
0 <= oldest_mark_index_< kMaxNonDurableEpochs
Note
ThreadLogBuffer guarantees that the thread does not create a new log when current_mark_index_ might reach "oldest_mark_index_ + kMaxNonDurableEpochs". Thus, "current_mark_index_ == oldest_mark_index_" means empty, not full.
Written by thread only. Read by logger and thread.

Definition at line 251 of file thread_log_buffer.hpp.

Referenced by assert_consistent(), foedus::log::ThreadLogBuffer::get_last_epoch(), foedus::log::ThreadLogBuffer::get_logs_to_write(), foedus::log::ThreadLogBuffer::initialize_once(), foedus::log::ThreadLogBuffer::on_log_written(), foedus::log::operator<<(), and ThreadLogBufferMeta().

uint64_t foedus::log::ThreadLogBufferMeta::offset_committed_

This marks the position upto which transaction logs are committed by the thread.

Log writers can safely read log entries and write them to log files up to this place. When the transaction commits, this value is advanced by the thread. The only possible update pattern to this variable is advance by this thread. Thus, the log writer can safely read this variable without any fence or lock thanks to regular (either old value or new value, never garbage) read of 64-bit.

Definition at line 221 of file thread_log_buffer.hpp.

Referenced by assert_consistent(), foedus::log::ThreadLogBuffer::discard_current_xct_log(), foedus::log::ThreadLogBuffer::get_logs_to_write(), foedus::log::ThreadLogBuffer::get_offset_committed(), foedus::log::ThreadLogBuffer::initialize_once(), foedus::log::operator<<(), foedus::log::ThreadLogBuffer::publish_committed_log(), ThreadLogBufferMeta(), and foedus::log::ThreadLogBuffer::wait_for_space().

uint64_t foedus::log::ThreadLogBufferMeta::offset_durable_

This marks the position upto which the log writer durably wrote out to log files.

Everything after this position must not be discarded because they are not yet durable. When the log writer reads log entries after here, writes them to log file, and calls fsync, this variable is advanced by the log writer. This variable is read by this thread to check the end of the circular buffer.

Definition at line 210 of file thread_log_buffer.hpp.

Referenced by assert_consistent(), foedus::log::ThreadLogBuffer::get_logs_to_write(), foedus::log::ThreadLogBuffer::get_offset_durable(), foedus::log::ThreadLogBuffer::initialize_once(), foedus::log::ThreadLogBuffer::on_log_written(), foedus::log::operator<<(), ThreadLogBufferMeta(), and foedus::log::ThreadLogBuffer::wait_for_space().

uint64_t foedus::log::ThreadLogBufferMeta::offset_head_

This marks the position where log entries start.

This private log buffer is a circular buffer where the head is eaten by log gleaner. However, log gleaner is okay to get behind, reading from log file instead (but slower). Thus, offset_head_ is advanced either by log gleaner or this thread. If the latter happens, log gleaner has to give up using in-memory logs and instead read from log files.

Definition at line 200 of file thread_log_buffer.hpp.

Referenced by assert_consistent(), foedus::log::ThreadLogBuffer::get_offset_head(), foedus::log::ThreadLogBuffer::head_to_tail_distance(), foedus::log::ThreadLogBuffer::initialize_once(), foedus::log::operator<<(), ThreadLogBufferMeta(), and foedus::log::ThreadLogBuffer::wait_for_space().

uint64_t foedus::log::ThreadLogBufferMeta::offset_tail_

The current cursor to which next log will be written.

This is the location the current transaction of this thread is writing to before commit. When the transaction commits, offset_committed_ catches up with this. When the transaction aborts, this value rolls back to offset_committed_. Only this thread reads/writes to this variable. No other threads access this.

Definition at line 231 of file thread_log_buffer.hpp.

Referenced by assert_consistent(), foedus::log::ThreadLogBuffer::discard_current_xct_log(), foedus::log::ThreadLogBuffer::get_offset_tail(), foedus::log::ThreadLogBuffer::head_to_tail_distance(), foedus::log::ThreadLogBuffer::initialize_once(), foedus::log::operator<<(), foedus::log::ThreadLogBuffer::publish_committed_log(), foedus::log::ThreadLogBuffer::reserve_new_log(), and ThreadLogBufferMeta().

uint32_t foedus::log::ThreadLogBufferMeta::oldest_mark_index_

Array index in thread_epoch_marks_ that indicates the oldest epoch mark to be consumed by the logger.

Invariant
0 <= oldest_mark_index_ < kMaxNonDurableEpochs
Note
Written by logger only. Read by logger and thread.

Definition at line 241 of file thread_log_buffer.hpp.

Referenced by assert_consistent(), foedus::log::ThreadLogBuffer::get_logs_to_write(), foedus::log::ThreadLogBuffer::initialize_once(), foedus::log::ThreadLogBuffer::on_log_written(), foedus::log::operator<<(), and ThreadLogBufferMeta().

uint16_t foedus::log::ThreadLogBufferMeta::padding1_

Definition at line 175 of file thread_log_buffer.hpp.

uint32_t foedus::log::ThreadLogBufferMeta::padding2_

Definition at line 176 of file thread_log_buffer.hpp.


The documentation for this struct was generated from the following files: