From d3a2e81566760f06c7b3e01b2b67e30356a4de22 Mon Sep 17 00:00:00 2001 From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Date: Mon, 19 Jun 2017 15:56:37 +0300 Subject: [PATCH] vinyl: make UPSERT prepare use of a cache as a hint Use a tuple cache to turn an UPSERT in the PREPARE, if the cache contains a tuple with the exact same key. The UPSERT can be applied to the cached tuples, because 1. The cache always contains only REPLACE statements; 2. The cache always contains only newest statements. Closes #2520 --- src/box/vinyl.c | 43 ++++++++++++++++++++++++++++++++-------- src/box/vy_cache.c | 8 +++++++- src/box/vy_cache.h | 5 ++++- test/vinyl/upsert.result | 2 +- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/box/vinyl.c b/src/box/vinyl.c index d0a2be9266..e05f6ea688 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -2644,7 +2644,7 @@ vy_index_commit_stmt(struct vy_index *index, struct vy_mem *mem, vy_index_commit_upsert(index, mem, stmt); /* Invalidate cache element. */ - vy_cache_on_write(&index->cache, stmt); + vy_cache_on_write(&index->cache, stmt, NULL); } /* @@ -2660,7 +2660,7 @@ vy_index_rollback_stmt(struct vy_index *index, struct vy_mem *mem, vy_mem_rollback_stmt(mem, stmt); /* Invalidate cache element. */ - vy_cache_on_write(&index->cache, stmt); + vy_cache_on_write(&index->cache, stmt, NULL); } /** @@ -2848,13 +2848,40 @@ vy_tx_write(struct vy_index *index, struct vy_mem *mem, assert(*region_stmt == NULL || vy_stmt_is_region_allocated(*region_stmt)); - int rc = vy_index_set(index, mem, stmt, region_stmt); - /* - * Invalidate cache element. - */ - vy_cache_on_write(&index->cache, stmt); - return rc; + * The UPSERT statement can be applied to the cached + * statement, because the cache always contains only + * newest REPLACE statements. In such a case the UPSERT, + * applied to the cached statement, can be inserted + * instead of the original UPSERT. + */ + if (vy_stmt_type(stmt) == IPROTO_UPSERT) { + struct tuple *deleted = NULL; + /* Invalidate cache element. */ + vy_cache_on_write(&index->cache, stmt, &deleted); + if (deleted != NULL) { + struct tuple *applied = + vy_apply_upsert(stmt, deleted, mem->key_def, + mem->format, mem->upsert_format, + false); + tuple_unref(deleted); + if (applied != NULL) { + assert(vy_stmt_type(applied) == IPROTO_REPLACE); + int rc = vy_index_set(index, mem, applied, + region_stmt); + tuple_unref(applied); + return rc; + } + /* + * Ignore a memory error, because it is + * not critical to apply the optimization. + */ + } + } else { + /* Invalidate cache element. */ + vy_cache_on_write(&index->cache, stmt, NULL); + } + return vy_index_set(index, mem, stmt, region_stmt); } /* {{{ Scheduler Task */ diff --git a/src/box/vy_cache.c b/src/box/vy_cache.c index e31aa25c70..48f480ced7 100644 --- a/src/box/vy_cache.c +++ b/src/box/vy_cache.c @@ -349,7 +349,8 @@ vy_cache_add(struct vy_cache *cache, struct tuple *stmt, } void -vy_cache_on_write(struct vy_cache *cache, const struct tuple *stmt) +vy_cache_on_write(struct vy_cache *cache, const struct tuple *stmt, + struct tuple **deleted) { vy_cache_gc(cache->env); bool exact = false; @@ -409,6 +410,11 @@ vy_cache_on_write(struct vy_cache *cache, const struct tuple *stmt) if (exact) { cache->version++; struct vy_cache_entry *to_delete = *entry; + assert(vy_stmt_type(to_delete->stmt) == IPROTO_REPLACE); + if (deleted != NULL) { + *deleted = to_delete->stmt; + tuple_ref(to_delete->stmt); + } vy_cache_tree_delete(&cache->cache_tree, to_delete); vy_cache_entry_delete(cache->env, to_delete); } diff --git a/src/box/vy_cache.h b/src/box/vy_cache.h index f472b14e01..0e7792b554 100644 --- a/src/box/vy_cache.h +++ b/src/box/vy_cache.h @@ -190,9 +190,12 @@ vy_cache_add(struct vy_cache *cache, struct tuple *stmt, * Invalidate possibly cached value due to its overwriting * @param cache - pointer to tuple cache. * @param stmt - overwritten statement. + * @param[out] deleted - If not NULL, then is set to deleted + * statement. */ void -vy_cache_on_write(struct vy_cache *cache, const struct tuple *stmt); +vy_cache_on_write(struct vy_cache *cache, const struct tuple *stmt, + struct tuple **deleted); /** diff --git a/test/vinyl/upsert.result b/test/vinyl/upsert.result index 9f48d6136a..0d11b71db4 100644 --- a/test/vinyl/upsert.result +++ b/test/vinyl/upsert.result @@ -739,7 +739,7 @@ new_stat = box.info.vinyl().performance["iterator"].run.lookup_count ... new_stat - old_stat --- -- 2 +- 1 ... old_stat = new_stat --- -- GitLab