diff --git a/src/box/vinyl.c b/src/box/vinyl.c index d0a2be926682fcdef65df789257e0fd316c657b4..e05f6ea6880b4aa0cf71aa580af07476e703e5f4 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 e31aa25c70b2fc96aeccfaf6d0241da95dc491ac..48f480ced76720e5de4a32113539963b47ddbae5 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 f472b14e01922fbc14ac40dd6ef21ae1a231ecbf..0e7792b554bd0f201dbb6e49f0b7a3456d09fbef 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 9f48d6136ae3d50926d0a27902015fcd976e83c2..0d11b71db4fb652d4ec7c68d93891c47fc8602ea 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 ---