@@ -426,6 +426,14 @@ void QList::Node::SetExternal(size_t offset, uint32_t size) {
426426 ext_size = size;
427427}
428428
429+ void QList::Node::Upload (QList* ql, std::string_view val) {
430+ entry = static_cast <unsigned char *>(zmalloc (val.size ()));
431+ memcpy (entry, val.data (), val.size ());
432+ ql->AdjustMallocSize (val.size ());
433+ ql->AdjustOffloadNodeCount (-1 );
434+ offloaded = 0 ;
435+ }
436+
429437void QList::SetPackedThreshold (unsigned threshold) {
430438 packed_threshold = threshold;
431439}
@@ -480,10 +488,9 @@ QList::QList(QList&& other) noexcept
480488 tiering_enabled_(other.tiering_enabled_),
481489 compress_(other.compress_),
482490 bookmark_count_(other.bookmark_count_),
483- num_offloaded_nodes_( other.num_offloaded_nodes_ ) {
491+ tiering_params_(std::move( other.tiering_params_) ) {
484492 other.head_ = nullptr ;
485493 other.len_ = other.count_ = 0 ;
486- other.num_offloaded_nodes_ = 0 ;
487494}
488495
489496QList::~QList () {
@@ -503,9 +510,9 @@ QList& QList::operator=(QList&& other) noexcept {
503510 tiering_enabled_ = other.tiering_enabled_ ;
504511 compress_ = other.compress_ ;
505512 bookmark_count_ = other.bookmark_count_ ;
506- num_offloaded_nodes_ = other.num_offloaded_nodes_ ;
507513 other.head_ = nullptr ;
508- other.len_ = other.count_ = other.num_offloaded_nodes_ = 0 ;
514+ other.len_ = other.count_ = 0 ;
515+ tiering_params_ = std::move (other.tiering_params_ );
509516 }
510517 return *this ;
511518}
@@ -519,7 +526,7 @@ void QList::Clear() noexcept {
519526 // If entry is offloaded we should skip freeing its memory.
520527 bool free_entry = current->offloaded == 0 ;
521528 if (tiering_enabled_ && (current->offloaded || current->io_pending )) {
522- CleanupOffloadedNode ( current);
529+ tiering_params_-> cleanup ( this , current);
523530 } else {
524531 if (current->encoding != QUICKLIST_NODE_ENCODING_RAW) {
525532 quicklistLZF* lzf = (quicklistLZF*)current->entry ;
@@ -540,7 +547,6 @@ void QList::Clear() noexcept {
540547 head_ = nullptr ;
541548 count_ = 0 ;
542549 malloc_size_ = 0 ;
543- num_offloaded_nodes_ = 0 ;
544550}
545551
546552void QList::Push (string_view value, Where where) {
@@ -915,6 +921,9 @@ void QList::Replace(Iterator it, std::string_view elem) {
915921
916922void QList::CoolOff (Node* node, uint32_t node_id) {
917923 if (tiering_enabled_) {
924+ uint32_t threshold = tiering_params_->node_depth_threshold ;
925+ uint32_t num_offloaded_nodes = tiering_params_->num_offloaded_nodes ;
926+
918927 // Dry run for offloading decision.
919928 // a. Node id is withing the offloadable depth - offload it if not already offloaded.
920929 // b. Node id is outside the offloadable depth - but we have too many nodes that are not
@@ -925,12 +934,11 @@ void QList::CoolOff(Node* node, uint32_t node_id) {
925934 // we won't need to traverse them again for "trivial" access patterns unless they
926935 // get accessed again. Another reason for missing offloaded nodes is that node_id can be
927936 // off due to merges (can be improved in future).
928- if (node_id >= tiering_node_depth_threshold_ &&
929- node_id + tiering_node_depth_threshold_ < len_) {
937+ if (node_id >= threshold && node_id + threshold < len_) {
930938 if (!node->offloaded && !node->io_pending ) {
931- OffloadNode ( node);
939+ tiering_params_-> offload ( this , node);
932940 }
933- } else if (num_offloaded_nodes_ * 2 + tiering_node_depth_threshold_ * 2 < len_) {
941+ } else if (num_offloaded_nodes * 2 + threshold * 2 < len_) {
934942 // We check `num_offloaded_nodes_ * 2` above to avoid frequent traversals.
935943 // So only when the gap between offloaded and non-offloaded nodes is large enough,
936944 // we do a traversal to offload more nodes.
@@ -940,16 +948,15 @@ void QList::CoolOff(Node* node, uint32_t node_id) {
940948
941949 // Traverse from both ends towards the middle as we expect more offloads towards the ends
942950 // due to usual access patterns of adding items via lpush/rpush.
943- while (traverse_node_id <= len_ / 2 &&
944- (num_offloaded_nodes_ + 2 * tiering_node_depth_threshold_) < len_) {
945- if (traverse_node_id >= tiering_node_depth_threshold_) {
951+ while (traverse_node_id <= len_ / 2 && (num_offloaded_nodes + 2 * threshold) < len_) {
952+ if (traverse_node_id >= threshold) {
946953 if (fw->offloaded == 0 && fw->io_pending == 0 ) {
947- OffloadNode ( fw);
954+ tiering_params_-> offload ( this , fw);
948955 }
949956
950957 // Avoid offloading the same node twice when fw and rev meet in the middle.
951958 if (rev != fw && rev->offloaded == 0 && rev->io_pending == 0 ) {
952- OffloadNode ( rev);
959+ tiering_params_-> offload ( this , rev);
953960 }
954961 }
955962 fw = fw->next ;
@@ -1047,12 +1054,12 @@ void QList::Materialize(Node* node) {
10471054
10481055 // Cancel stash in progress before loading.
10491056 if (node->io_pending ) {
1050- CleanupOffloadedNode ( node);
1057+ tiering_params_-> cleanup ( this , node);
10511058 }
10521059
10531060 // Load the offloaded node data back into memory.
10541061 if (node->offloaded ) {
1055- ReadOffloadedNode ( node);
1062+ tiering_params_-> load ( this , node);
10561063 }
10571064
10581065 DCHECK (!node->offloaded );
@@ -1195,7 +1202,7 @@ void QList::DelNode(Node* node) {
11951202 }
11961203
11971204 if (tiering_enabled_ && (node->offloaded || node->io_pending )) {
1198- CleanupOffloadedNode ( node);
1205+ tiering_params_-> cleanup ( this , node);
11991206 }
12001207
12011208 /* If we deleted a node within our compress depth, we
@@ -1243,18 +1250,22 @@ bool QList::DelPackedIndex(Node* node, uint8_t* p) {
12431250 return false ;
12441251}
12451252
1246- void QList::OffloadNode (Node* node) const {
1247- DCHECK (tiering_enabled_ && node->offloaded == 0 && node->io_pending == 0 );
1248- stats.offload_requests ++;
1249- node->io_pending = 1 ;
1250- }
1251-
1252- void QList::ReadOffloadedNode (QList::Node* node) const {
1253- stats.onload_requests ++;
1254- }
1255-
1256- void QList::CleanupOffloadedNode (QList::Node* node) const {
1257- node->io_pending = 0 ;
1253+ void QList::SetDbIndex (DbIndex db_id) {
1254+ if (db_id_ == db_id) {
1255+ return ;
1256+ }
1257+ // With tiering enabled, we materialize all offloaded nodes before reassigning the db_id.
1258+ // This is suboptimal: pending nodes could be canceled, and fully offloaded nodes only need
1259+ // a statistics update — they don't depend on db_id, only on their storage offset.
1260+ if (tiering_enabled_ && tiering_params_->num_offloaded_nodes > 0 ) {
1261+ Node* node = head_;
1262+ while (node) {
1263+ Node* next = node->next ;
1264+ Materialize (node);
1265+ node = (next == head_) ? nullptr : next;
1266+ }
1267+ }
1268+ db_id_ = db_id;
12581269}
12591270
12601271void QList::InitIteratorEntry (Iterator* it) const {
0 commit comments