diff --git a/src/box/vinyl.c b/src/box/vinyl.c index 5da1c4bc3a7c3f062b771ec6bda51e93d9dbbdeb..a0d2078a8263a8fb19eee2eddd410ee90a5eab6e 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -1290,6 +1290,10 @@ vy_get_by_secondary_tuple(struct vy_lsm *lsm, struct vy_tx *tx, say_warn("%s: key %s missing in primary index", vy_lsm_name(lsm), vy_stmt_str(tuple)); } + + if ((*rv)->vlsn == INT64_MAX) + vy_cache_add(&lsm->pk->cache, *result, NULL, tuple, ITER_EQ); + return 0; } @@ -1336,6 +1340,8 @@ vy_get(struct vy_lsm *lsm, struct vy_tx *tx, } else { *result = tuple; } + if ((*rv)->vlsn == INT64_MAX) + vy_cache_add(&lsm->cache, *result, NULL, key, ITER_EQ); return 0; } @@ -1352,6 +1358,8 @@ vy_get(struct vy_lsm *lsm, struct vy_tx *tx, if (rc != 0 || *result != NULL) break; } + if (rc == 0) + vy_read_iterator_cache_add(&itr, *result); vy_read_iterator_close(&itr); return rc; } @@ -3719,6 +3727,7 @@ vinyl_iterator_primary_next(struct iterator *base, struct tuple **ret) goto fail; if (vy_read_iterator_next(&it->iterator, ret) != 0) goto fail; + vy_read_iterator_cache_add(&it->iterator, *ret); if (*ret == NULL) { /* EOF. Close the iterator immediately. */ vinyl_iterator_close(it); @@ -3748,6 +3757,7 @@ vinyl_iterator_secondary_next(struct iterator *base, struct tuple **ret) if (tuple == NULL) { /* EOF. Close the iterator immediately. */ + vy_read_iterator_cache_add(&it->iterator, NULL); vinyl_iterator_close(it); *ret = NULL; return 0; @@ -3767,6 +3777,7 @@ vinyl_iterator_secondary_next(struct iterator *base, struct tuple **ret) goto fail; if (*ret == NULL) goto next; + vy_read_iterator_cache_add(&it->iterator, *ret); tuple_bless(*ret); tuple_unref(*ret); return 0; diff --git a/src/box/vy_point_lookup.c b/src/box/vy_point_lookup.c index f2261fdf394ab89d851e7c2060396a0b02719d01..5e43340b90fa76d154e610bde9545c29764a9bec 100644 --- a/src/box/vy_point_lookup.c +++ b/src/box/vy_point_lookup.c @@ -280,11 +280,8 @@ vy_point_lookup(struct vy_lsm *lsm, struct vy_tx *tx, if (rc != 0) return -1; - if (*ret != NULL) { + if (*ret != NULL) vy_stmt_counter_acct_tuple(&lsm->stat.get, *ret); - if ((*rv)->vlsn == INT64_MAX) - vy_cache_add(&lsm->cache, *ret, NULL, key, ITER_EQ); - } double latency = ev_monotonic_now(loop()) - start_time; latency_collect(&lsm->stat.latency, latency); diff --git a/src/box/vy_read_iterator.c b/src/box/vy_read_iterator.c index 160bb899440283a5ad193d73b4a672d3056fa031..954fc0df6a8ca84cc04de44eba1439caeba5fad4 100644 --- a/src/box/vy_read_iterator.c +++ b/src/box/vy_read_iterator.c @@ -845,24 +845,17 @@ vy_read_iterator_next(struct vy_read_iterator *itr, struct tuple **result) ev_tstamp start_time = ev_monotonic_now(loop()); struct vy_lsm *lsm = itr->lsm; - struct tuple *stmt, *prev_stmt; + struct tuple *stmt; - /* - * Remember the statement returned by the last iteration. - * We will need it to update the cache. - */ - prev_stmt = itr->last_stmt; - if (prev_stmt != NULL) - tuple_ref(prev_stmt); - else /* first iteration */ - lsm->stat.lookup++; + if (itr->last_stmt == NULL) + lsm->stat.lookup++; /* first iteration */ next_key: if (vy_read_iterator_advance(itr) != 0) - goto err; + return -1; if (vy_read_iterator_apply_history(itr, &stmt) != 0) - goto err; + return -1; if (vy_read_iterator_track_read(itr, stmt) != 0) - goto err; + return -1; if (itr->last_stmt != NULL) tuple_unref(itr->last_stmt); @@ -877,9 +870,9 @@ vy_read_iterator_next(struct vy_read_iterator *itr, struct tuple **result) * previous + current tuple as an unbroken chain. */ if (vy_stmt_lsn(stmt) == INT64_MAX) { - if (prev_stmt != NULL) - tuple_unref(prev_stmt); - prev_stmt = NULL; + if (itr->last_cached_stmt != NULL) + tuple_unref(itr->last_cached_stmt); + itr->last_cached_stmt = NULL; } goto next_key; } @@ -887,18 +880,6 @@ vy_read_iterator_next(struct vy_read_iterator *itr, struct tuple **result) vy_stmt_type(stmt) == IPROTO_INSERT || vy_stmt_type(stmt) == IPROTO_REPLACE); - /* - * Store the result in the cache provided we are reading - * the latest data. - */ - if ((**itr->read_view).vlsn == INT64_MAX) { - vy_cache_add(&lsm->cache, stmt, prev_stmt, - itr->key, itr->iterator_type); - } - if (prev_stmt != NULL) - tuple_unref(prev_stmt); - - /* Update LSM tree stats. */ if (stmt != NULL) vy_stmt_counter_acct_tuple(&lsm->stat.get, stmt); @@ -914,10 +895,24 @@ vy_read_iterator_next(struct vy_read_iterator *itr, struct tuple **result) *result = stmt; return 0; -err: - if (prev_stmt != NULL) - tuple_unref(prev_stmt); - return -1; +} + +void +vy_read_iterator_cache_add(struct vy_read_iterator *itr, struct tuple *stmt) +{ + if ((**itr->read_view).vlsn != INT64_MAX) { + if (itr->last_cached_stmt != NULL) + tuple_unref(itr->last_cached_stmt); + itr->last_cached_stmt = NULL; + return; + } + vy_cache_add(&itr->lsm->cache, stmt, itr->last_cached_stmt, + itr->key, itr->iterator_type); + if (stmt != NULL) + tuple_ref(stmt); + if (itr->last_cached_stmt != NULL) + tuple_unref(itr->last_cached_stmt); + itr->last_cached_stmt = stmt; } /** @@ -928,6 +923,8 @@ vy_read_iterator_close(struct vy_read_iterator *itr) { if (itr->last_stmt != NULL) tuple_unref(itr->last_stmt); + if (itr->last_cached_stmt != NULL) + tuple_unref(itr->last_cached_stmt); vy_read_iterator_cleanup(itr); free(itr->src); TRASH(itr); diff --git a/src/box/vy_read_iterator.h b/src/box/vy_read_iterator.h index 2cac1087c09089e40ce0431ea1a1f481d086b922..baab8859231af8a72ff832652365beac3a199e0b 100644 --- a/src/box/vy_read_iterator.h +++ b/src/box/vy_read_iterator.h @@ -64,6 +64,11 @@ struct vy_read_iterator { bool need_check_eq; /** Last statement returned by vy_read_iterator_next(). */ struct tuple *last_stmt; + /** + * Last statement added to the tuple cache by + * vy_read_iterator_cache_add(). + */ + struct tuple *last_cached_stmt; /** * Copy of lsm->range_tree_version. * Used for detecting range tree changes. @@ -141,6 +146,25 @@ vy_read_iterator_open(struct vy_read_iterator *itr, struct vy_lsm *lsm, NODISCARD int vy_read_iterator_next(struct vy_read_iterator *itr, struct tuple **result); +/** + * Add the last tuple returned by the read iterator to the cache. + * @param itr Read iterator + * @param stmt Last tuple returned by the iterator. + * + * We use a separate function for populating the cache rather than + * doing that right in vy_read_iterator_next() so that we can store + * full tuples in a secondary index cache, thus saving some memory. + * + * Usage pattern: + * - Call vy_read_iterator_next() to get a partial tuple. + * - Call vy_point_lookup() to get the full tuple corresponding + * to the partial tuple returned by the iterator. + * - Call vy_read_iterator_cache_add() on the full tuple to add + * the result to the cache. + */ +void +vy_read_iterator_cache_add(struct vy_read_iterator *itr, struct tuple *stmt); + /** * Close the iterator and free resources. */