Esempio n. 1
0
DumpFileSortedBuffer::DumpFileSortedBuffer(
  fs::DirectIoFile* file, memory::AlignedMemorySlice io_buffer)
  : SortedBuffer(
    reinterpret_cast<char*>(io_buffer.get_block()),
    io_buffer.get_size(),
    fs::file_size(file->get_path())),
    file_(file),
    io_buffer_(io_buffer) {
  ASSERT_ND(buffer_size_ % kAlignment == 0);
  ASSERT_ND(total_size_ % kAlignment == 0);
}
Esempio n. 2
0
inline uint32_t ConstDiv::rem64(uint64_t n, uint32_t d, uint64_t q) const {
#ifndef NDEBUG
  ASSERT_ND(d == d_);
#endif  // NDEBUG
  ASSERT_ND(n / d == q);
  if (flags_ & kFlagPowerOfTwo) {
    return n & ((1ULL << d_highest_bits_) - 1ULL);
  } else {
    return n - d * q;
  }
}
Esempio n. 3
0
xct::McsBlockIndex ThreadPimpl::mcs_initial_lock(xct::McsLock* mcs_lock) {
  assert_mcs_aligned(mcs_lock);
  ASSERT_ND(!mcs_lock->is_locked());
  // so far we allow only 2^16 MCS blocks per transaction. we might increase later.
  ASSERT_ND(current_xct_.get_mcs_block_current() < 0xFFFFU);
  xct::McsBlockIndex block_index = current_xct_.increment_mcs_block_current();
  ASSERT_ND(block_index > 0);
  mcs_init_block(mcs_lock, block_index, false);
  mcs_lock->reset(id_, block_index);
  assorted::memory_fence_acq_rel();
  return block_index;
}
Esempio n. 4
0
ErrorCode ThreadPimpl::follow_page_pointers_for_write_batch(
  uint16_t batch_size,
  storage::VolatilePageInit page_initializer,
  storage::DualPagePointer** pointers,
  storage::Page** parents,
  const uint16_t* index_in_parents,
  storage::Page** out) {
  // REMINDER: Remember that it might be parents == out. It's not an issue in this function, tho.
  // this method is not quite batched as it doesn't need to be.
  // still, less branches because we can assume all of them need a writable volatile page.
  for (uint16_t b = 0; b < batch_size; ++b) {
    storage::DualPagePointer* pointer = pointers[b];
    if (pointer == nullptr) {
      out[b] = nullptr;
      continue;
    }
    ASSERT_ND(!parents[b]->get_header().snapshot_);
    storage::Page** page = out + b;
    storage::VolatilePagePointer volatile_pointer = pointer->volatile_pointer_;
    if (!volatile_pointer.is_null()) {
      *page = global_volatile_page_resolver_.resolve_offset(volatile_pointer);
    } else if (pointer->snapshot_pointer_ == 0) {
      // we need a volatile page. so construct it from snapshot
      CHECK_ERROR_CODE(install_a_volatile_page(pointer, page));
    } else {
      ASSERT_ND(page_initializer);
      // we must not install a new volatile page in snapshot page. We must not hit this case.
      memory::PagePoolOffset offset = core_memory_->grab_free_volatile_page();
      if (UNLIKELY(offset == 0)) {
        return kErrorCodeMemoryNoFreePages;
      }
      storage::Page* new_page = local_volatile_page_resolver_.resolve_offset_newpage(offset);
      storage::VolatilePagePointer new_page_id;
      new_page_id.components.numa_node = numa_node_;
      new_page_id.components.offset = offset;
      storage::VolatilePageInitArguments args = {
        holder_,
        new_page_id,
        new_page,
        parents[b],
        index_in_parents[b]
      };
      page_initializer(args);
      storage::assert_valid_volatile_page(new_page, offset);
      ASSERT_ND(new_page->get_header().snapshot_ == false);

      *page = place_a_new_volatile_page(offset, pointer);
    }
    ASSERT_ND(out[b] != nullptr);
  }
  return kErrorCodeOk;
}
Esempio n. 5
0
uint32_t PagePoolOffsetAndEpochChunk::get_safe_offset_count(const Epoch& threshold) const {
  ASSERT_ND(is_sorted());
  OffsetAndEpoch dummy;
  dummy.safe_epoch_ = threshold.value();
  struct CompareEpoch {
    bool operator() (const OffsetAndEpoch& left, const OffsetAndEpoch& right) {
      return Epoch(left.safe_epoch_) < Epoch(right.safe_epoch_);
    }
  };
  const OffsetAndEpoch* result = std::lower_bound(chunk_, chunk_ + size_, dummy, CompareEpoch());
  ASSERT_ND(result);
  ASSERT_ND(result - chunk_ <= size_);
  return result - chunk_;
}
Esempio n. 6
0
void PagePoolOffsetAndEpochChunk::move_to(PagePoolOffset* destination, uint32_t count) {
  ASSERT_ND(size_ >= count);
  // we can't do memcpy. Just copy one by one
  for (uint32_t i = 0; i < count; ++i) {
    destination[i] = chunk_[i].offset_;
  }
  // Also, unlike PagePoolOffsetChunk, we copied from the head (we have to because epoch matters).
  // So, we also have to move remainings to the beginning
  if (size_ > count) {
    std::memmove(chunk_, chunk_ + count, (size_ - count) * sizeof(OffsetAndEpoch));
  }
  size_ -= count;
  ASSERT_ND(is_sorted());
}
bool LogMapper::add_new_bucket(storage::StorageId storage_id) {
  if (buckets_allocated_count_ >= buckets_memory_.get_size() / sizeof(Bucket)) {
    // we allocated all buckets_memory_! we have to flush the buckets now.
    // this shouldn't happen often.
    LOG(WARNING) << to_string() << " ran out of buckets_memory_, so it has to flush buckets before"
      " processing one IO buffer. This shouldn't happen often. check your log_mapper_bucket_kb_"
      " setting. this=" << *this;
    return false;
  }

  Bucket* base_address = reinterpret_cast<Bucket*>(buckets_memory_.get_block());
  Bucket* new_bucket = base_address + buckets_allocated_count_;
  ++buckets_allocated_count_;
  new_bucket->storage_id_ = storage_id;
  new_bucket->counts_ = 0;
  new_bucket->next_bucket_ = nullptr;

  BucketHashList* hashlist = find_storage_hashlist(storage_id);
  if (hashlist) {
    // just add this as a new tail
    ASSERT_ND(hashlist->storage_id_ == storage_id);
    ASSERT_ND(hashlist->tail_->is_full());
    hashlist->tail_->next_bucket_ = new_bucket;
    hashlist->tail_ = new_bucket;
    ++hashlist->bucket_counts_;
  } else {
    // we don't even have a linked list for this.
    // If this happens often, maybe we should have 65536 hash buckets...
    if (hashlist_allocated_count_ >= kBucketHashListMaxCount) {
      LOG(WARNING) << to_string() << " ran out of hashlist memory, so it has to flush buckets now"
        " This shouldn't happen often. We must consider increasing kBucketHashListMaxCount."
        " this=" << *this;
      return false;
    }

    // allocate from the pool
    BucketHashList* new_hashlist =
      reinterpret_cast<BucketHashList*>(tmp_hashlist_buffer_slice_.get_block())
      + hashlist_allocated_count_;
    ++hashlist_allocated_count_;

    new_hashlist->storage_id_ = storage_id;
    new_hashlist->head_ = new_bucket;
    new_hashlist->tail_ = new_bucket;
    new_hashlist->bucket_counts_ = 1;

    add_storage_hashlist(new_hashlist);
  }
  return true;
}
void LogMapper::send_bucket_partition_general(
  const Bucket* bucket,
  storage::StorageType storage_type,
  storage::PartitionId partition,
  const BufferPosition* positions) {
  uint64_t written = 0;
  uint32_t log_count = 0;
  uint32_t shortest_key_length = 0xFFFF;
  uint32_t longest_key_length = 0;
  // stitch the log entries in send buffer
  char* send_buffer = reinterpret_cast<char*>(tmp_send_buffer_slice_.get_block());
  const char* io_base = reinterpret_cast<const char*>(io_buffer_.get_block());
  ASSERT_ND(tmp_send_buffer_slice_.get_size() == kSendBufferSize);

  for (uint32_t i = 0; i < bucket->counts_; ++i) {
    uint64_t pos = from_buffer_position(positions[i]);
    const log::LogHeader* header = reinterpret_cast<const log::LogHeader*>(io_base + pos);
    ASSERT_ND(header->storage_id_ == bucket->storage_id_);
    uint16_t log_length = header->log_length_;
    ASSERT_ND(log_length > 0);
    ASSERT_ND(log_length % 8 == 0);
    if (written + log_length > kSendBufferSize) {
      // buffer full. send out.
      send_bucket_partition_buffer(
        bucket,
        partition,
        send_buffer,
        log_count,
        written,
        shortest_key_length,
        longest_key_length);
      log_count = 0;
      written = 0;
      shortest_key_length = 0xFFFF;
      longest_key_length = 0;
    }
    std::memcpy(send_buffer + written, header, header->log_length_);
    written += header->log_length_;
    ++log_count;
    update_key_lengthes(header, storage_type, &shortest_key_length, &longest_key_length);
  }
  send_bucket_partition_buffer(
    bucket,
    partition,
    send_buffer,
    log_count,
    written,
    shortest_key_length,
    longest_key_length);
}
Esempio n. 9
0
xct::McsBlockIndex Thread::mcs_acquire_lock_batch(xct::McsLock** mcs_locks, uint16_t batch_size) {
  // lock in address order. so, no deadlock possible
  // we have to lock them whether the record is deleted or not. all physical records.
  xct::McsBlockIndex head_lock_index = 0;
  for (uint16_t i = 0; i < batch_size; ++i) {
    xct::McsBlockIndex block = pimpl_->mcs_acquire_lock(mcs_locks[i]);
    ASSERT_ND(block > 0);
    if (i == 0) {
      head_lock_index = block;
    } else {
      ASSERT_ND(head_lock_index + i == block);
    }
  }
  return head_lock_index;
}
Esempio n. 10
0
ErrorStack inserts_varlen_task(const proc::ProcArguments& args) {
  EXPECT_EQ(sizeof(uint32_t), args.input_len_);
  uint32_t id = *reinterpret_cast<const uint32_t*>(args.input_buffer_);
  EXPECT_NE(id, 2U);

  thread::Thread* context = args.context_;
  storage::hash::HashStorage hash(args.engine_, kName);
  ASSERT_ND(hash.exists());
  xct::XctManager* xct_manager = args.engine_->get_xct_manager();
  Epoch commit_epoch;

  WRAP_ERROR_CODE(xct_manager->begin_xct(context, xct::kSerializable));

  char buffer[16];
  std::memset(buffer, 0, sizeof(buffer));
  for (uint32_t i = 0; i < kRecords / 2U; ++i) {
    uint64_t rec = id * kRecords / 2U + i;
    // first 8 bytes, mod 17 to have next layers.
    assorted::write_bigendian<uint64_t>(static_cast<uint64_t>(rec % 17U), buffer);
    // and 1-4 bytes of decimal representation in text
    std::string str = std::to_string(rec);
    std::memcpy(buffer + sizeof(uint64_t), str.data(), str.size());
    uint16_t len = sizeof(uint64_t) + str.size();
    uint64_t data = rec + kDataAddendum;
    ErrorCode ret = hash.insert_record(context, buffer, len, &data, sizeof(data));
    EXPECT_EQ(kErrorCodeOk, ret) << i;
  }

  // CHECK_ERROR(hash.debugout_single_thread(args.engine_));
  WRAP_ERROR_CODE(xct_manager->precommit_xct(context, &commit_epoch));
  // CHECK_ERROR(hash.debugout_single_thread(args.engine_));
  WRAP_ERROR_CODE(xct_manager->wait_for_commit(commit_epoch));
  return kRetOk;
}
Esempio n. 11
0
ErrorStack verify_varlen_task(const proc::ProcArguments& args) {
  thread::Thread* context = args.context_;
  storage::hash::HashStorage hash(args.engine_, kName);
  ASSERT_ND(hash.exists());
  CHECK_ERROR(hash.verify_single_thread(context));
  // CHECK_ERROR(hash.debugout_single_thread(args.engine_));
  xct::XctManager* xct_manager = args.engine_->get_xct_manager();
  WRAP_ERROR_CODE(xct_manager->begin_xct(context, xct::kSerializable));

  char buffer[16];
  std::memset(buffer, 0, sizeof(buffer));
  for (uint32_t i = 0; i < kRecords; ++i) {
    uint64_t rec = i;
    assorted::write_bigendian<uint64_t>(static_cast<uint64_t>(rec % 17U), buffer);
    std::string str = std::to_string(rec);
    std::memcpy(buffer + sizeof(uint64_t), str.data(), str.size());
    uint16_t len = sizeof(uint64_t) + str.size();

    uint64_t data;
    uint16_t capacity = sizeof(data);
    ErrorCode ret = hash.get_record(context, buffer, len, &data, &capacity);
    EXPECT_EQ(kErrorCodeOk, ret) << i;
    EXPECT_EQ(i + kDataAddendum, data) << i;
    EXPECT_EQ(sizeof(data), capacity) << i;
  }

  Epoch commit_epoch;
  ErrorCode committed = xct_manager->precommit_xct(context, &commit_epoch);
  EXPECT_EQ(kErrorCodeOk, committed);
  return kRetOk;
}
Esempio n. 12
0
void HashDataPage::release_pages_recursive(
  const memory::GlobalVolatilePageResolver& page_resolver,
  memory::PageReleaseBatch* batch) {
  if (next_page_.volatile_pointer_.components.offset != 0) {
    HashDataPage* next = reinterpret_cast<HashDataPage*>(
      page_resolver.resolve_offset(next_page_.volatile_pointer_));
    ASSERT_ND(next->header().get_in_layer_level() == 0);
    ASSERT_ND(next->get_bin() == get_bin());
    next->release_pages_recursive(page_resolver, batch);
    next_page_.volatile_pointer_.components.offset = 0;
  }

  VolatilePagePointer volatile_id;
  volatile_id.word = header().page_id_;
  batch->release(volatile_id);
}
ErrorStack verify_task(const proc::ProcArguments& args) {
  thread::Thread* context = args.context_;
  storage::masstree::MasstreeStorage masstree(args.engine_, kName);
  ASSERT_ND(masstree.exists());
  CHECK_ERROR(masstree.verify_single_thread(context));
  xct::XctManager* xct_manager = args.engine_->get_xct_manager();
  WRAP_ERROR_CODE(xct_manager->begin_xct(context, xct::kSerializable));

  for (uint32_t i = 0; i < kRecords; ++i) {
    uint64_t rec = i;
    storage::masstree::KeySlice slice = storage::masstree::normalize_primitive<uint64_t>(rec);
    uint64_t data;
    uint16_t capacity = sizeof(data);
    ErrorCode ret = masstree.get_record_normalized(context, slice, &data, &capacity, true);
/*
    if (ret != kErrorCodeOk || rec != data) {
      CHECK_ERROR(masstree.verify_single_thread(context));
      CHECK_ERROR(masstree.debugout_single_thread(args.engine_));
      std::cout << "asdasd" << std::endl;
    }
*/
    EXPECT_EQ(kErrorCodeOk, ret) << i;
    EXPECT_EQ(rec, data) << i;
    EXPECT_EQ(sizeof(data), capacity) << i;
  }

  Epoch commit_epoch;
  ErrorCode committed = xct_manager->precommit_xct(context, &commit_epoch);
  EXPECT_EQ(kErrorCodeOk, committed);
  return kRetOk;
}
Esempio n. 14
0
ErrorStack TpccLoadTask::load_customers_in_district(Wid wid, Did did) {
  LOG(INFO) << "Loading Customer for DID=" << static_cast<int>(did) << ", WID=" << wid
    << ": " << engine_->get_memory_manager()->dump_free_memory_stat();

  // insert to customers_secondary at the end after sorting
  memory::AlignedMemory secondary_keys_buffer;
  struct Secondary {
    char  last_[17];      // +17 -> 17
    char  first_[17];     // +17 -> 34
    char  padding_[2];    // +2 -> 36
    Cid   cid_;           // +4 -> 40
    static bool compare(const Secondary &left, const Secondary& right) ALWAYS_INLINE {
      int cmp = std::memcmp(left.last_, right.last_, sizeof(left.last_));
      if (cmp < 0) {
        return true;
      } else if (cmp > 0) {
        return false;
      }
      cmp = std::memcmp(left.first_, right.first_, sizeof(left.first_));
      if (cmp < 0) {
        return true;
      } else if (cmp > 0) {
        return false;
      }
        ASSERT_ND(left.cid_ != right.cid_);
      if (left.cid_ < right.cid_) {
        return true;
      } else {
        return false;
      }
    }
  };
Esempio n. 15
0
TpccLoadTask::TpccLoadTask(char* ctime_buffer) {
  // Initialize timestamp (for date columns)
  time_t t_clock;
  ::time(&t_clock);
  timestamp_ = ::ctime_r(&t_clock, ctime_buffer);
  ASSERT_ND(timestamp_);
}
Esempio n. 16
0
void ThreadPimpl::flush_retired_volatile_page(
  uint16_t node,
  Epoch current_epoch,
  memory::PagePoolOffsetAndEpochChunk* chunk) {
  if (chunk->size() == 0) {
    return;
  }
  uint32_t safe_count = chunk->get_safe_offset_count(current_epoch);
  while (safe_count < chunk->size() / 10U) {
    LOG(WARNING) << "Thread-" << id_ << " can return only "
      << safe_count << " out of " << chunk->size()
      << " retired pages to node-" << node  << " in epoch=" << current_epoch
      << ". This means the thread received so many retired pages in a short time period."
      << " Will adavance an epoch to safely return the retired pages."
      << " This should be a rare event.";
    engine_->get_xct_manager()->advance_current_global_epoch();
    current_epoch = engine_->get_xct_manager()->get_current_global_epoch();
    LOG(INFO) << "okay, advanced epoch. now we should be able to return more pages";
    safe_count = chunk->get_safe_offset_count(current_epoch);
  }

  VLOG(0) << "Thread-" << id_ << " batch-returning retired volatile pages to node-" << node
    << " safe_count/count=" << safe_count << "/" << chunk->size() << ". epoch=" << current_epoch;
  memory::PagePool* volatile_pool
    = engine_->get_memory_manager()->get_node_memory(node)->get_volatile_pool();
  volatile_pool->release(safe_count, chunk);
  ASSERT_ND(!chunk->full());
}
Esempio n. 17
0
DivvyupPageGrabBatch::DivvyupPageGrabBatch(Engine* engine)
: engine_(engine), node_count_(engine->get_options().thread_.group_count_) {
  chunks_ = new PagePoolOffsetChunk[node_count_];
  for (uint16_t node = 0; node < node_count_; ++node) {
    ASSERT_ND(chunks_[node].empty());
  }
}
Esempio n. 18
0
void PagePoolOffsetChunk::move_to(PagePoolOffset* destination, uint32_t count) {
  ASSERT_ND(size_ >= count);
  std::memcpy(destination, chunk_ + (size_ - count), count * sizeof(PagePoolOffset));
  // we move from the tail of this chunk. so, just decrementing the count is enough.
  // no need to move the remainings back to the beginning
  size_ -= count;
}
Esempio n. 19
0
 void get_assignments(uint32_t count, uint32_t* result_from, uint32_t* result_to) const {
   if (load_threads_ == 1U) {
     ASSERT_ND(thread_ordinal_ == 0);
     *result_from = 0;
     *result_to = count;
   } else {
     ASSERT_ND(thread_ordinal_ < load_threads_);
     uint32_t div = count / load_threads_;
     ASSERT_ND(div > 0);
     EXPECT_GT(div, 0);
     *result_from = div * thread_ordinal_;
     *result_to = div * (thread_ordinal_ + 1U);
     if (thread_ordinal_ + 1U == load_threads_) {
       *result_to = count;
     }
   }
 }
Esempio n. 20
0
void HashIntermediatePage::initialize_volatile_page(
  StorageId storage_id,
  VolatilePagePointer page_id,
  const HashIntermediatePage* parent,
  uint8_t level,
  HashBin start_bin) {
  std::memset(this, 0, kPageSize);
  header_.init_volatile(page_id, storage_id, kHashIntermediatePageType);
  bin_range_.begin_ = start_bin;
  bin_range_.end_ = bin_range_.begin_ + kHashMaxBins[level + 1U];
  header_.set_in_layer_level(level);
  if (parent) {
    ASSERT_ND(parent->get_level() > 0);
    ASSERT_ND(level + 1U == parent->get_level());
    ASSERT_ND(parent->bin_range_.contains(bin_range_));
  }
}
Esempio n. 21
0
void PagePoolOffsetDynamicChunk::move_to(PagePoolOffset* destination, uint32_t count) {
  ASSERT_ND(size_ >= count);
  std::memcpy(destination, chunk_, count * sizeof(PagePoolOffset));

  // Skip the consumed entries
  chunk_ += count;
  size_ -= count;
}
bool ImpersonateSession::is_running() const {
  ASSERT_ND(thread_->get_control_block()->current_ticket_ >= ticket_);
  if (thread_->get_control_block()->current_ticket_ != ticket_) {
    return false;
  }
  return (thread_->get_control_block()->status_ == kWaitingForExecution ||
    thread_->get_control_block()->status_ == kRunningTask);
}
Esempio n. 23
0
DataPageSlotIndex HashDataPage::search_key(
  HashValue hash,
  const BloomFilterFingerprint& fingerprint,
  const char* key,
  uint16_t key_length,
  uint16_t record_count,
  xct::XctId* observed) const {
  // invariant checks
  ASSERT_ND(hash == hashinate(key, key_length));
  ASSERT_ND(DataPageBloomFilter::extract_fingerprint(hash) == fingerprint);
  ASSERT_ND(record_count <= get_record_count());  // it must be increasing.

  // check bloom filter first.
  if (!bloom_filter_.contains(fingerprint)) {
    return kSlotNotFound;
  }

  // then most likely this page contains it. let's check one by one.
  for (uint16_t i = 0; i < record_count; ++i) {
    const Slot& s = get_slot(i);
    if (LIKELY(s.hash_ != hash) || s.key_length_ != key_length) {
      continue;
    }
    xct::XctId xid = s.tid_.xct_id_;
    if (xid.is_moved()) {
      // not so rare. this happens.
      DVLOG(1) << "Hash matched, but the record was moved";
      continue;
    }

    const char* data = record_from_offset(s.offset_);
    if (s.key_length_ == key_length && std::memcmp(data, key, key_length) == 0) {
      *observed = xid;
      return i;
    }
    // hash matched, but key didn't match? wow, that's rare
    DLOG(INFO) << "Hash matched, but key didn't match. interesting. hash="
      << assorted::Hex(hash, 16) << ", key=" << assorted::HexString(std::string(key, key_length))
      << ", key_slot="  << assorted::HexString(std::string(data, s.key_length_));
  }

  // should be 1~2%
  DVLOG(0) << "Nope, bloom filter contained it, but key not found in this page. false positive";
  return kSlotNotFound;
}
Esempio n. 24
0
void hash_data_volatile_page_init(const VolatilePageInitArguments& args) {
  ASSERT_ND(args.parent_);
  ASSERT_ND(args.page_);
  StorageId storage_id = args.parent_->get_header().storage_id_;
  HashDataPage* page = reinterpret_cast<HashDataPage*>(args.page_);
  PageType parent_type = args.parent_->get_header().get_page_type();
  uint64_t bin;
  if (parent_type == kHashIntermediatePageType) {
    const HashIntermediatePage* parent
      = reinterpret_cast<const HashIntermediatePage*>(args.parent_);

    ASSERT_ND(args.index_in_parent_ < kHashIntermediatePageFanout);
    ASSERT_ND(parent->get_level() == 0);
    ASSERT_ND(parent->get_bin_range().length() == kHashIntermediatePageFanout);
    bin = parent->get_bin_range().begin_ + args.index_in_parent_;
  } else {
    ASSERT_ND(parent_type == kHashDataPageType);
    ASSERT_ND(args.index_in_parent_ == 0);
    const HashDataPage* parent = reinterpret_cast<const HashDataPage*>(args.parent_);
    bin = parent->get_bin();
  }
  HashStorage storage(args.context_->get_engine(), storage_id);
  uint8_t bin_bits = storage.get_bin_bits();
  uint8_t bin_shifts = storage.get_bin_shifts();
  page->initialize_volatile_page(storage_id, args.page_id, args.parent_, bin, bin_bits, bin_shifts);
}
Esempio n. 25
0
void MetaLogger::meta_logger_main() {
  LOG(INFO) << "Meta-logger started";
  while (!stop_requested_) {
    {
      uint64_t demand = control_block_->logger_wakeup_.acquire_ticket();
      if (!stop_requested_ && !control_block_->has_waiting_log()) {
        VLOG(0) << "Meta-logger going to sleep";
        control_block_->logger_wakeup_.timedwait(demand, 100000ULL);
      }
    }
    VLOG(0) << "Meta-logger woke up";
    if (stop_requested_) {
      break;
    } else if (!control_block_->has_waiting_log()) {
      continue;
    }

    // we observed buffer_used_ > 0 BEFORE the fence. Now we can read buffer_ safely.
    assorted::memory_fence_acq_rel();
    ASSERT_ND(control_block_->buffer_used_ > 0);
    uint32_t write_size = sizeof(control_block_->buffer_);
    ASSERT_ND(control_block_->buffer_used_ <= write_size);
    LOG(INFO) << "Meta-logger got a log (" << control_block_->buffer_used_ << " b) to write out";
    FillerLogType* filler = reinterpret_cast<FillerLogType*>(
      control_block_->buffer_ + control_block_->buffer_used_);
    filler->populate(write_size - control_block_->buffer_used_);
    ErrorCode er = current_file_->write_raw(write_size, control_block_->buffer_);
    if (er != kErrorCodeOk) {
      LOG(FATAL) << "Meta-logger couldn't write a log. error=" << get_error_message(er)
        << ", os_error=" << assorted::os_error();
    }

    // also fsync on the file and parent. every, single, time.
    // we don't care performance on metadata logger. just make it simple and robust.
    bool synced = fs::fsync(current_file_->get_path(), true);
    if (!synced) {
      LOG(FATAL) << "Meta-logger couldn't fsync. os_error=" << assorted::os_error();
    }
    assorted::memory_fence_release();
    control_block_->buffer_used_ = 0;
    assorted::memory_fence_release();
    control_block_->durable_offset_ += write_size;
  }
  LOG(INFO) << "Meta-logger terminated";
}
Esempio n. 26
0
void hash_intermediate_volatile_page_init(const VolatilePageInitArguments& args) {
  ASSERT_ND(args.parent_);  // because this is always called for non-root pages.
  ASSERT_ND(args.page_);
  ASSERT_ND(args.index_in_parent_ < kHashIntermediatePageFanout);
  StorageId storage_id = args.parent_->get_header().storage_id_;
  HashIntermediatePage* page = reinterpret_cast<HashIntermediatePage*>(args.page_);

  ASSERT_ND(args.parent_->get_header().get_page_type() == kHashIntermediatePageType);
  const HashIntermediatePage* parent = reinterpret_cast<const HashIntermediatePage*>(args.parent_);

  ASSERT_ND(parent->get_level() > 0);
  parent->assert_range();
  HashBin interval = kHashMaxBins[parent->get_level()];
  HashBin parent_begin = parent->get_bin_range().begin_;
  HashBin begin = parent_begin + args.index_in_parent_ * interval;
  uint8_t level = parent->get_level() - 1U;
  page->initialize_volatile_page(storage_id, args.page_id, parent, level, begin);
}
Esempio n. 27
0
bool PagePoolOffsetAndEpochChunk::is_sorted() const {
  for (uint32_t i = 1; i < size_; ++i) {
    ASSERT_ND(chunk_[i].offset_);
    if (Epoch(chunk_[i - 1U].safe_epoch_) > Epoch(chunk_[i].safe_epoch_)) {
      return false;
    }
  }
  return true;
}
Esempio n. 28
0
void RoundRobinPageGrabBatch::release_all() {
  if (chunk_.empty()) {
    return;
  }

  engine_->get_memory_manager()->get_node_memory(current_node_)->get_volatile_pool()->release(
    chunk_.size(), &chunk_);
  ASSERT_ND(chunk_.empty());
}
Esempio n. 29
0
 // Compose a key for read/update/scan
 YcsbKey& build_rus_key(uint32_t total_thread_count) {
   // Choose a high-bits field first. Then take a look at that worker's local counter
   auto high = rnd_record_select_.uniform_within(0, total_thread_count - 1);
   auto cnt = channel_->peek_local_key_counter(engine_, high);
   // The load should have inserted at least one record on behalf of this worker
   ASSERT_ND(cnt > 0);
   auto low = rnd_record_select_.uniform_within(0, cnt - 1);
   return key_arena_.build(high, low);
 }
Esempio n. 30
0
void DivvyupPageGrabBatch::release_all() {
  for (uint16_t node = 0; node < node_count_; ++node) {
    if (chunks_[node].empty()) {
      continue;
    }
    engine_->get_memory_manager()->get_node_memory(node)->get_volatile_pool()->release(
      chunks_[node].size(), chunks_ + node);
    ASSERT_ND(chunks_[node].empty());
  }
}