summaryrefslogtreecommitdiff
path: root/ft
diff options
context:
space:
mode:
authorJohn Esmet <john.esmet@gmail.com>2014-05-27 15:49:06 -0400
committerJohn Esmet <john.esmet@gmail.com>2014-06-14 20:25:34 -0400
commite59ed34aa0ebbad40063a19fca06ede2c2b4539d (patch)
tree47a89d6fec95a49b7d39fc94bfa7f91590ef062a /ft
parenta8cacc5437e09f7a04cb494074392d2bfa46c573 (diff)
downloadmariadb-git-e59ed34aa0ebbad40063a19fca06ede2c2b4539d.tar.gz
FT-197 Store cachefiles in OMTs only, remove linked list pointers
Diffstat (limited to 'ft')
-rw-r--r--ft/cachetable-internal.h6
-rw-r--r--ft/cachetable.cc292
-rw-r--r--ft/tests/cachetable-checkpointer-class.cc40
3 files changed, 150 insertions, 188 deletions
diff --git a/ft/cachetable-internal.h b/ft/cachetable-internal.h
index 0e0d1ad0f64..9eb3ec66568 100644
--- a/ft/cachetable-internal.h
+++ b/ft/cachetable-internal.h
@@ -178,8 +178,6 @@ class pair_list;
// Maps to a file on disk.
//
struct cachefile {
- CACHEFILE next;
- CACHEFILE prev;
// these next two fields are protected by cachetable's list lock
// they are managed whenever we add or remove a pair from
// the cachetable. As of Riddler, this linked list is only used to
@@ -439,14 +437,12 @@ public:
bool evict_some_stale_pair(evictor* ev);
void free_stale_data(evictor* ev);
// access to these fields are protected by the lock
- CACHEFILE m_active_head; // head of CACHEFILEs that are active
- CACHEFILE m_stale_head; // head of CACHEFILEs that are stale
- CACHEFILE m_stale_tail; // tail of CACHEFILEs that are stale
FILENUM m_next_filenum_to_use;
uint32_t m_next_hash_id_to_use;
toku_pthread_rwlock_t m_lock; // this field is publoc so we are still POD
toku::omt<CACHEFILE> m_active_filenum;
toku::omt<CACHEFILE> m_active_fileid;
+ toku::omt<CACHEFILE> m_stale_fileid;
private:
CACHEFILE find_cachefile_in_list_unlocked(CACHEFILE start, struct fileid* fileid);
};
diff --git a/ft/cachetable.cc b/ft/cachetable.cc
index d7c734cc5fd..d57592286a4 100644
--- a/ft/cachetable.cc
+++ b/ft/cachetable.cc
@@ -4414,43 +4414,48 @@ void checkpointer::increment_num_txns() {
m_checkpoint_num_txns++;
}
-//
-// Update the user data in any cachefiles in our checkpoint list.
-//
-void checkpointer::update_cachefiles() {
- CACHEFILE cf;
- for(cf = m_cf_list->m_active_head; cf; cf=cf->next) {
+struct iterate_begin_checkpoint {
+ LSN lsn_of_checkpoint_in_progress;
+ iterate_begin_checkpoint(LSN lsn) : lsn_of_checkpoint_in_progress(lsn) { }
+ static int fn(const CACHEFILE &cf, const uint32_t UU(idx), struct iterate_begin_checkpoint *info) {
assert(cf->begin_checkpoint_userdata);
if (cf->for_checkpoint) {
- cf->begin_checkpoint_userdata(m_lsn_of_checkpoint_in_progress,
- cf->userdata);
+ cf->begin_checkpoint_userdata(info->lsn_of_checkpoint_in_progress, cf->userdata);
}
+ return 0;
}
+};
+
+//
+// Update the user data in any cachefiles in our checkpoint list.
+//
+void checkpointer::update_cachefiles() {
+ struct iterate_begin_checkpoint iterate(m_lsn_of_checkpoint_in_progress);
+ int r = m_cf_list->m_active_fileid.iterate<struct iterate_begin_checkpoint,
+ iterate_begin_checkpoint::fn>(&iterate);
+ assert_zero(r);
}
+struct iterate_note_pin {
+ static int fn(const CACHEFILE &cf, uint32_t UU(idx), void **UU(extra)) {
+ assert(cf->note_pin_by_checkpoint);
+ cf->note_pin_by_checkpoint(cf, cf->userdata);
+ cf->for_checkpoint = true;
+ return 0;
+ }
+};
+
//
// Sets up and kicks off a checkpoint.
//
void checkpointer::begin_checkpoint() {
// 1. Initialize the accountability counters.
- m_checkpoint_num_files = 0;
m_checkpoint_num_txns = 0;
// 2. Make list of cachefiles to be included in the checkpoint.
- // TODO: <CER> How do we remove the non-lock cachetable reference here?
m_cf_list->read_lock();
- for (CACHEFILE cf = m_cf_list->m_active_head; cf; cf = cf->next) {
- // The caller must serialize open, close, and begin checkpoint.
- // So we should never see a closing cachefile here.
- // <CER> Is there an assert we can add here?
-
- // Putting this check here so that this method may be called
- // by cachetable tests.
- assert(cf->note_pin_by_checkpoint);
- cf->note_pin_by_checkpoint(cf, cf->userdata);
- cf->for_checkpoint = true;
- m_checkpoint_num_files++;
- }
+ m_cf_list->m_active_fileid.iterate<void *, iterate_note_pin::fn>(nullptr);
+ m_checkpoint_num_files = m_cf_list->m_active_fileid.size();
m_cf_list->read_unlock();
// 3. Create log entries for this checkpoint.
@@ -4475,6 +4480,14 @@ void checkpointer::begin_checkpoint() {
m_list->write_pending_exp_unlock();
}
+struct iterate_log_fassociate {
+ static int fn(const CACHEFILE &cf, uint32_t UU(idx), void **UU(extra)) {
+ assert(cf->log_fassociate_during_checkpoint);
+ cf->log_fassociate_during_checkpoint(cf, cf->userdata);
+ return 0;
+ }
+};
+
//
// Assuming the logger exists, this will write out the folloing
// information to the log.
@@ -4498,10 +4511,7 @@ void checkpointer::log_begin_checkpoint() {
m_lsn_of_checkpoint_in_progress = begin_lsn;
// Log the list of open dictionaries.
- for (CACHEFILE cf = m_cf_list->m_active_head; cf; cf = cf->next) {
- assert(cf->log_fassociate_during_checkpoint);
- cf->log_fassociate_during_checkpoint(cf, cf->userdata);
- }
+ m_cf_list->m_active_fileid.iterate<void *, iterate_log_fassociate::fn>(nullptr);
// Write open transactions to the log.
r = toku_txn_manager_iter_over_live_txns(
@@ -4576,17 +4586,29 @@ void checkpointer::end_checkpoint(void (*testcallback_f)(void*), void* testextr
toku_free(checkpoint_cfs);
}
-void checkpointer::fill_checkpoint_cfs(CACHEFILE* checkpoint_cfs) {
- m_cf_list->read_lock();
- uint32_t curr_index = 0;
- for (CACHEFILE cf = m_cf_list->m_active_head; cf; cf = cf->next) {
+struct iterate_checkpoint_cfs {
+ CACHEFILE *checkpoint_cfs;
+ uint32_t checkpoint_num_files;
+ uint32_t curr_index;
+ iterate_checkpoint_cfs(CACHEFILE *cfs, uint32_t num_files) :
+ checkpoint_cfs(cfs), checkpoint_num_files(num_files), curr_index(0) {
+ }
+ static int fn(const CACHEFILE &cf, uint32_t UU(idx), struct iterate_checkpoint_cfs *info) {
if (cf->for_checkpoint) {
- assert(curr_index < m_checkpoint_num_files);
- checkpoint_cfs[curr_index] = cf;
- curr_index++;
+ assert(info->curr_index < info->checkpoint_num_files);
+ info->checkpoint_cfs[info->curr_index] = cf;
+ info->curr_index++;
}
+ return 0;
}
- assert(curr_index == m_checkpoint_num_files);
+};
+
+void checkpointer::fill_checkpoint_cfs(CACHEFILE* checkpoint_cfs) {
+ struct iterate_checkpoint_cfs iterate(checkpoint_cfs, m_checkpoint_num_files);
+
+ m_cf_list->read_lock();
+ m_cf_list->m_active_fileid.iterate<struct iterate_checkpoint_cfs, iterate_checkpoint_cfs::fn>(&iterate);
+ assert(iterate.curr_index == m_checkpoint_num_files);
m_cf_list->read_unlock();
}
@@ -4671,19 +4693,18 @@ void checkpointer::remove_cachefiles(CACHEFILE* checkpoint_cfs) {
static_assert(std::is_pod<cachefile_list>::value, "cachefile_list isn't POD");
void cachefile_list::init() {
- m_active_head = NULL;
- m_stale_head = NULL;
- m_stale_tail = NULL;
m_next_filenum_to_use.fileid = 0;
m_next_hash_id_to_use = 0;
toku_pthread_rwlock_init(&m_lock, NULL);
m_active_filenum.create();
m_active_fileid.create();
+ m_stale_fileid.create();
}
void cachefile_list::destroy() {
m_active_filenum.destroy();
m_active_fileid.destroy();
+ m_stale_fileid.destroy();
toku_pthread_rwlock_destroy(&m_lock);
}
@@ -4702,34 +4723,31 @@ void cachefile_list::write_lock() {
void cachefile_list::write_unlock() {
toku_pthread_rwlock_wrunlock(&m_lock);
}
-int cachefile_list::cachefile_of_iname_in_env(const char *iname_in_env, CACHEFILE *cf) {
- read_lock();
- CACHEFILE extant;
- int r;
- r = ENOENT;
- for (extant = m_active_head; extant; extant = extant->next) {
- if (extant->fname_in_env &&
- !strcmp(extant->fname_in_env, iname_in_env)) {
- *cf = extant;
- r = 0;
- break;
+
+struct iterate_find_iname {
+ const char *iname_in_env;
+ CACHEFILE found_cf;
+ iterate_find_iname(const char *iname) : iname_in_env(iname), found_cf(nullptr) { }
+ static int fn(const CACHEFILE &cf, uint32_t UU(idx), struct iterate_find_iname *info) {
+ if (cf->fname_in_env && strcmp(cf->fname_in_env, info->iname_in_env) == 0) {
+ info->found_cf = cf;
+ return -1;
}
+ return 0;
}
- read_unlock();
- return r;
-}
+};
+
+int cachefile_list::cachefile_of_iname_in_env(const char *iname_in_env, CACHEFILE *cf) {
+ struct iterate_find_iname iterate(iname_in_env);
-int cachefile_list::cachefile_of_filenum(FILENUM filenum, CACHEFILE *cf) {
read_lock();
- CACHEFILE extant;
- int r = ENOENT;
- *cf = NULL;
- for (extant = m_active_head; extant; extant = extant->next) {
- if (extant->filenum.fileid==filenum.fileid) {
- *cf = extant;
- r = 0;
- break;
- }
+ int r = m_active_fileid.iterate<iterate_find_iname, iterate_find_iname::fn>(&iterate);
+ if (iterate.found_cf != nullptr) {
+ assert(strcmp(iterate.found_cf->fname_in_env, iname_in_env) == 0);
+ *cf = iterate.found_cf;
+ r = 0;
+ } else {
+ r = ENOENT;
}
read_unlock();
return r;
@@ -4746,20 +4764,23 @@ static int cachefile_find_by_filenum(const CACHEFILE &a_cf, const FILENUM &b) {
}
}
+int cachefile_list::cachefile_of_filenum(FILENUM filenum, CACHEFILE *cf) {
+ read_lock();
+ int r = m_active_filenum.find_zero<FILENUM, cachefile_find_by_filenum>(filenum, cf, nullptr);
+ if (r == DB_NOTFOUND) {
+ r = ENOENT;
+ } else {
+ invariant_zero(r);
+ }
+ read_unlock();
+ return r;
+}
+
static int cachefile_find_by_fileid(const CACHEFILE &a_cf, const struct fileid &b) {
return toku_fileid_cmp(a_cf->fileid, b);
}
void cachefile_list::add_cf_unlocked(CACHEFILE cf) {
- invariant(cf->next == NULL);
- invariant(cf->prev == NULL);
- cf->next = m_active_head;
- cf->prev = NULL;
- if (m_active_head) {
- m_active_head->prev = cf;
- }
- m_active_head = cf;
-
int r;
r = m_active_filenum.insert<FILENUM, cachefile_find_by_filenum>(cf, cf->filenum, nullptr);
assert_zero(r);
@@ -4769,36 +4790,13 @@ void cachefile_list::add_cf_unlocked(CACHEFILE cf) {
void cachefile_list::add_stale_cf(CACHEFILE cf) {
write_lock();
- invariant(cf->next == NULL);
- invariant(cf->prev == NULL);
-
- cf->next = m_stale_head;
- cf->prev = NULL;
- if (m_stale_head) {
- m_stale_head->prev = cf;
- }
- m_stale_head = cf;
- if (m_stale_tail == NULL) {
- m_stale_tail = cf;
- }
+ int r = m_stale_fileid.insert<struct fileid, cachefile_find_by_fileid>(cf, cf->fileid, nullptr);
+ assert_zero(r);
write_unlock();
}
void cachefile_list::remove_cf(CACHEFILE cf) {
write_lock();
- invariant(m_active_head != NULL);
- if (cf->next) {
- cf->next->prev = cf->prev;
- }
- if (cf->prev) {
- cf->prev->next = cf->next;
- }
- if (cf == m_active_head) {
- invariant(cf->prev == NULL);
- m_active_head = cf->next;
- }
- cf->prev = NULL;
- cf->next = NULL;
uint32_t idx;
int r;
@@ -4816,24 +4814,12 @@ void cachefile_list::remove_cf(CACHEFILE cf) {
}
void cachefile_list::remove_stale_cf_unlocked(CACHEFILE cf) {
- invariant(m_stale_head != NULL);
- invariant(m_stale_tail != NULL);
- if (cf->next) {
- cf->next->prev = cf->prev;
- }
- if (cf->prev) {
- cf->prev->next = cf->next;
- }
- if (cf == m_stale_head) {
- invariant(cf->prev == NULL);
- m_stale_head = cf->next;
- }
- if (cf == m_stale_tail) {
- invariant(cf->next == NULL);
- m_stale_tail = cf->prev;
- }
- cf->prev = NULL;
- cf->next = NULL;
+ uint32_t idx;
+ int r;
+ r = m_stale_fileid.find_zero<struct fileid, cachefile_find_by_fileid>(cf->fileid, nullptr, &idx);
+ assert_zero(r);
+ r = m_stale_fileid.delete_at(idx);
+ assert_zero(r);
}
FILENUM cachefile_list::reserve_filenum() {
@@ -4849,11 +4835,6 @@ FILENUM cachefile_list::reserve_filenum() {
break;
}
FILENUM filenum = m_next_filenum_to_use;
-#if TOKU_DEBUG_PARANOID
- for (CACHEFILE extant = m_active_head; extant; extant = extant->next) {
- assert(filenum.fileid != extant->filenum.fileid);
- }
-#endif
m_next_filenum_to_use.fileid++;
write_unlock();
return filenum;
@@ -4865,91 +4846,77 @@ uint32_t cachefile_list::get_new_hash_id_unlocked() {
return retval;
}
-CACHEFILE cachefile_list::find_cachefile_in_list_unlocked(
- CACHEFILE start,
- struct fileid* fileid
- )
-{
- CACHEFILE retval = NULL;
- for (CACHEFILE extant = start; extant; extant = extant->next) {
- if (toku_fileids_are_equal(&extant->fileid, fileid)) {
- // Clients must serialize cachefile open, close, and unlink
- // So, during open, we should never see a closing cachefile
- // or one that has been marked as unlink on close.
- assert(!extant->unlink_on_close);
- retval = extant;
- goto exit;
- }
- }
-exit:
- return retval;
-}
-
CACHEFILE cachefile_list::find_cachefile_unlocked(struct fileid* fileid) {
CACHEFILE cf = nullptr;
int r = m_active_fileid.find_zero<struct fileid, cachefile_find_by_fileid>(*fileid, &cf, nullptr);
if (r == 0) {
assert(!cf->unlink_on_close);
}
-#if TOKU_DEBUG_PARANOID
- assert(cf == find_cachefile_in_list_unlocked(m_active_head, fileid));
-#endif
return cf;
}
CACHEFILE cachefile_list::find_stale_cachefile_unlocked(struct fileid* fileid) {
- return find_cachefile_in_list_unlocked(m_stale_head, fileid);
+ CACHEFILE cf = nullptr;
+ int r = m_stale_fileid.find_zero<struct fileid, cachefile_find_by_fileid>(*fileid, &cf, nullptr);
+ if (r == 0) {
+ assert(!cf->unlink_on_close);
+ }
+ return cf;
}
void cachefile_list::verify_unused_filenum(FILENUM filenum) {
int r = m_active_filenum.find_zero<FILENUM, cachefile_find_by_filenum>(filenum, nullptr, nullptr);
assert(r == DB_NOTFOUND);
-#if TOKU_DEBUG_PARANOID
- for (CACHEFILE extant = m_active_head; extant; extant = extant->next) {
- invariant(extant->filenum.fileid != filenum.fileid);
- }
-#endif
}
// returns true if some eviction ran, false otherwise
bool cachefile_list::evict_some_stale_pair(evictor* ev) {
- PAIR p = NULL;
- CACHEFILE cf_to_destroy = NULL;
write_lock();
- if (m_stale_tail == NULL) {
+ if (m_stale_fileid.size() == 0) {
write_unlock();
return false;
}
- p = m_stale_tail->cf_head;
+
+ CACHEFILE stale_cf = nullptr;
+ int r = m_stale_fileid.fetch(0, &stale_cf);
+ assert_zero(r);
+
// we should not have a cf in the stale list
// that does not have any pairs
+ PAIR p = stale_cf->cf_head;
paranoid_invariant(p != NULL);
-
evict_pair_from_cachefile(p);
// now that we have evicted something,
// let's check if the cachefile is needed anymore
- if (m_stale_tail->cf_head == NULL) {
- cf_to_destroy = m_stale_tail;
- remove_stale_cf_unlocked(m_stale_tail);
+ //
+ // it is not needed if the latest eviction caused
+ // the cf_head for that cf to become null
+ bool destroy_cf = stale_cf->cf_head == nullptr;
+ if (destroy_cf) {
+ remove_stale_cf_unlocked(stale_cf);
}
write_unlock();
ev->remove_pair_attr(p->attr);
cachetable_free_pair(p);
- if (cf_to_destroy) {
- cachefile_destroy(cf_to_destroy);
+ if (destroy_cf) {
+ cachefile_destroy(stale_cf);
}
return true;
}
void cachefile_list::free_stale_data(evictor* ev) {
write_lock();
- while (m_stale_tail != NULL) {
- PAIR p = m_stale_tail->cf_head;
+ while (m_stale_fileid.size() != 0) {
+ CACHEFILE stale_cf = nullptr;
+ int r = m_stale_fileid.fetch(0, &stale_cf);
+ assert_zero(r);
+
// we should not have a cf in the stale list
// that does not have any pairs
+ PAIR p = stale_cf->cf_head;
paranoid_invariant(p != NULL);
evict_pair_from_cachefile(p);
@@ -4958,10 +4925,9 @@ void cachefile_list::free_stale_data(evictor* ev) {
// now that we have evicted something,
// let's check if the cachefile is needed anymore
- if (m_stale_tail->cf_head == NULL) {
- CACHEFILE cf_to_destroy = m_stale_tail;
- remove_stale_cf_unlocked(m_stale_tail);
- cachefile_destroy(cf_to_destroy);
+ if (stale_cf->cf_head == NULL) {
+ remove_stale_cf_unlocked(stale_cf);
+ cachefile_destroy(stale_cf);
}
}
write_unlock();
diff --git a/ft/tests/cachetable-checkpointer-class.cc b/ft/tests/cachetable-checkpointer-class.cc
index c2adc202fb5..fa950d3972a 100644
--- a/ft/tests/cachetable-checkpointer-class.cc
+++ b/ft/tests/cachetable-checkpointer-class.cc
@@ -112,6 +112,14 @@ struct checkpointer_test {
uint32_t k);
};
+static void init_cachefile(CACHEFILE cf, int which_cf, bool for_checkpoint) {
+ memset(cf, 0, sizeof(*cf));
+ create_dummy_functions(cf);
+ cf->fileid = { 0, (unsigned) which_cf };
+ cf->filenum = { (unsigned) which_cf };
+ cf->for_checkpoint = for_checkpoint;
+}
+
//------------------------------------------------------------------------------
// test_begin_checkpoint() -
//
@@ -135,33 +143,28 @@ void checkpointer_test::test_begin_checkpoint() {
// 2. Call checkpoint with ONE cachefile.
//cachefile cf;
struct cachefile cf;
- cf.next = NULL;
- cf.for_checkpoint = false;
- m_cp.m_cf_list->m_active_head = &cf;
- create_dummy_functions(&cf);
+ init_cachefile(&cf, 0, false);
+ m_cp.m_cf_list->add_cf_unlocked(&cf);
m_cp.begin_checkpoint();
assert(m_cp.m_checkpoint_num_files == 1);
assert(cf.for_checkpoint == true);
+ m_cp.m_cf_list->remove_cf(&cf);
// 3. Call checkpoint with MANY cachefiles.
const uint32_t count = 3;
struct cachefile cfs[count];
- m_cp.m_cf_list->m_active_head = &cfs[0];
for (uint32_t i = 0; i < count; ++i) {
- cfs[i].for_checkpoint = false;
+ init_cachefile(&cfs[i], i, false);
create_dummy_functions(&cfs[i]);
- if (i == count - 1) {
- cfs[i].next = NULL;
- } else {
- cfs[i].next = &cfs[i + 1];
- }
+ m_cp.m_cf_list->add_cf_unlocked(&cfs[i]);
}
m_cp.begin_checkpoint();
assert(m_cp.m_checkpoint_num_files == count);
for (uint32_t i = 0; i < count; ++i) {
assert(cfs[i].for_checkpoint == true);
+ cfl.remove_cf(&cfs[i]);
}
ctbl.list.destroy();
m_cp.destroy();
@@ -195,10 +198,8 @@ void checkpointer_test::test_pending_bits() {
//
struct cachefile cf;
cf.cachetable = &ctbl;
- memset(&cf, 0, sizeof(cf));
- cf.next = NULL;
- cf.for_checkpoint = true;
- m_cp.m_cf_list->m_active_head = &cf;
+ init_cachefile(&cf, 0, true);
+ m_cp.m_cf_list->add_cf_unlocked(&cf);
create_dummy_functions(&cf);
CACHEKEY k;
@@ -258,6 +259,7 @@ void checkpointer_test::test_pending_bits() {
ctbl.list.destroy();
m_cp.destroy();
+ cfl.remove_cf(&cf);
cfl.destroy();
}
@@ -337,14 +339,11 @@ void checkpointer_test::test_end_checkpoint() {
cfl.init();
struct cachefile cf;
- memset(&cf, 0, sizeof(cf));
- cf.next = NULL;
- cf.for_checkpoint = true;
- create_dummy_functions(&cf);
+ init_cachefile(&cf, 0, true);
ZERO_STRUCT(m_cp);
m_cp.init(&ctbl.list, NULL, &ctbl.ev, &cfl);
- m_cp.m_cf_list->m_active_head = &cf;
+ m_cp.m_cf_list->add_cf_unlocked(&cf);
// 2. Add data before running checkpoint.
const uint32_t count = 6;
@@ -394,6 +393,7 @@ void checkpointer_test::test_end_checkpoint() {
assert(pp);
m_cp.m_list->evict_completely(pp);
}
+ cfl.remove_cf(&cf);
m_cp.destroy();
ctbl.list.destroy();
cfl.destroy();