diff --git a/src/box/key_def.c b/src/box/key_def.c index 1b00945cab70dd9680b830ebab912ee9fc17c134..9411ade39ff84f0a00b099af5453a04c8932a36c 100644 --- a/src/box/key_def.c +++ b/src/box/key_def.c @@ -142,7 +142,8 @@ key_def_set_part(struct key_def *def, uint32_t part_no, uint32_t fieldno, enum field_type type, enum on_conflict_action nullable_action, struct coll *coll, uint32_t coll_id, enum sort_order sort_order, const char *path, - uint32_t path_len, char **path_pool) + uint32_t path_len, char **path_pool, int32_t offset_slot, + uint64_t format_epoch) { assert(part_no < def->part_count); assert(type < field_type_MAX); @@ -154,6 +155,8 @@ key_def_set_part(struct key_def *def, uint32_t part_no, uint32_t fieldno, def->parts[part_no].coll = coll; def->parts[part_no].coll_id = coll_id; def->parts[part_no].sort_order = sort_order; + def->parts[part_no].offset_slot_cache = offset_slot; + def->parts[part_no].format_epoch = format_epoch; if (path != NULL) { assert(path_pool != NULL); def->parts[part_no].path = *path_pool; @@ -202,7 +205,7 @@ key_def_new(const struct key_part_def *parts, uint32_t part_count) key_def_set_part(def, i, part->fieldno, part->type, part->nullable_action, coll, part->coll_id, part->sort_order, part->path, path_len, - &path_pool); + &path_pool, TUPLE_OFFSET_SLOT_NIL, 0); } assert(path_pool == (char *)def + sz); key_def_set_cmp(def); @@ -256,7 +259,7 @@ box_key_def_new(uint32_t *fields, uint32_t *types, uint32_t part_count) (enum field_type)types[item], ON_CONFLICT_ACTION_DEFAULT, NULL, COLL_NONE, SORT_ORDER_ASC, NULL, 0, - NULL); + NULL, TUPLE_OFFSET_SLOT_NIL, 0); } key_def_set_cmp(key_def); return key_def; @@ -666,7 +669,8 @@ key_def_merge(const struct key_def *first, const struct key_def *second) key_def_set_part(new_def, pos++, part->fieldno, part->type, part->nullable_action, part->coll, part->coll_id, part->sort_order, part->path, - part->path_len, &path_pool); + part->path_len, &path_pool, + part->offset_slot_cache, part->format_epoch); } /* Set-append second key def's part to the new key def. */ @@ -678,7 +682,8 @@ key_def_merge(const struct key_def *first, const struct key_def *second) key_def_set_part(new_def, pos++, part->fieldno, part->type, part->nullable_action, part->coll, part->coll_id, part->sort_order, part->path, - part->path_len, &path_pool); + part->path_len, &path_pool, + part->offset_slot_cache, part->format_epoch); } assert(path_pool == (char *)new_def + sz); key_def_set_cmp(new_def); diff --git a/src/box/key_def.h b/src/box/key_def.h index 678d1f070f8e65b64b5c2a5d343261c7cab69398..85bed92bbff8d1fe618292e678bdff774be513aa 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -97,6 +97,20 @@ struct key_part { char *path; /** The length of JSON path. */ uint32_t path_len; + /** + * Epoch of the tuple format the offset slot cached in + * this part is valid for, see tuple_format::epoch. + */ + uint64_t format_epoch; + /** + * Cached value of the offset slot corresponding to + * the indexed field (tuple_field::offset_slot). + * Valid only if key_part::format_epoch equals the epoch + * of the tuple format. This value is updated in + * tuple_field_by_part_raw to always store the + * offset corresponding to the last used tuple format. + */ + int32_t offset_slot_cache; }; struct key_def; diff --git a/src/box/tuple.h b/src/box/tuple.h index c3cd689fd6f832c619a3ec54599ee6bb1e6a8b87..d2da267132bcc04e12d787cb75d4ce5462f9a6d4 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -528,7 +528,7 @@ tuple_field_by_path(const struct tuple *tuple, uint32_t fieldno, { return tuple_field_raw_by_path(tuple_format(tuple), tuple_data(tuple), tuple_field_map(tuple), fieldno, - path, path_len); + path, path_len, NULL); } /** diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c index d9c408495844a6f2a6059377cdc932fd918128fb..2d9b71ee1bd470566cdef33f55cc0bbda4c8f232 100644 --- a/src/box/tuple_format.c +++ b/src/box/tuple_format.c @@ -41,6 +41,7 @@ struct tuple_format **tuple_formats; static intptr_t recycled_format_ids = FORMAT_ID_NIL; static uint32_t formats_size = 0, formats_capacity = 0; +static uint64_t formats_epoch = 0; /** * Find in format1::fields the field by format2_field's JSON path. @@ -623,6 +624,7 @@ tuple_format_alloc(struct key_def * const *keys, uint16_t key_count, format->index_field_count = index_field_count; format->exact_field_count = 0; format->min_field_count = 0; + format->epoch = 0; return format; error: tuple_format_destroy_fields(format); @@ -735,6 +737,7 @@ tuple_format_new(struct tuple_format_vtab *vtab, void *engine, format->is_temporary = is_temporary; format->is_ephemeral = is_ephemeral; format->exact_field_count = exact_field_count; + format->epoch = ++formats_epoch; if (tuple_format_create(format, keys, key_count, space_fields, space_field_count) < 0) goto err; @@ -1205,5 +1208,5 @@ tuple_field_raw_by_full_path(struct tuple_format *format, const char *tuple, } return tuple_field_raw_by_path(format, tuple, field_map, fieldno, path + lexer.offset, - path_len - lexer.offset); + path_len - lexer.offset, NULL); } diff --git a/src/box/tuple_format.h b/src/box/tuple_format.h index d4b53195b0b8eb4b85d533c0939df386f393e8cd..01ed97ae1712499e2c60fc0dee9f80dbef58465b 100644 --- a/src/box/tuple_format.h +++ b/src/box/tuple_format.h @@ -149,6 +149,12 @@ struct tuple_format { * ephemeral spaces. */ uint32_t hash; + /** + * Counter that grows incrementally on space rebuild + * used for caching offset slot in key_part, for more + * details see key_part::offset_slot_cache. + */ + uint64_t epoch; /** Reference counter */ int refs; /** @@ -421,26 +427,43 @@ tuple_field_go_to_path(const char **data, const char *path, uint32_t path_len); * @param field_map Tuple field map. * @param path Relative JSON path to field. * @param path_len Length of @a path. + * @param offset_slot_hint The pointer to a variable that contains + * an offset slot. May be NULL. + * If specified AND value by pointer is + * not TUPLE_OFFSET_SLOT_NIL is used to + * access data in a single operation. + * Else it is initialized with offset_slot + * of format field by path. */ static inline const char * tuple_field_raw_by_path(struct tuple_format *format, const char *tuple, const uint32_t *field_map, uint32_t fieldno, - const char *path, uint32_t path_len) + const char *path, uint32_t path_len, + int32_t *offset_slot_hint) { + int32_t offset_slot; + if (offset_slot_hint != NULL && + *offset_slot_hint != TUPLE_OFFSET_SLOT_NIL) { + offset_slot = *offset_slot_hint; + goto offset_slot_access; + } if (likely(fieldno < format->index_field_count)) { + struct tuple_field *field; if (path == NULL && fieldno == 0) { mp_decode_array(&tuple); return tuple; } - struct tuple_field *field = - tuple_format_field_by_path(format, fieldno, path, + field = tuple_format_field_by_path(format, fieldno, path, path_len); assert(field != NULL || path != NULL); if (path != NULL && field == NULL) goto parse; - int32_t offset_slot = field->offset_slot; + offset_slot = field->offset_slot; if (offset_slot == TUPLE_OFFSET_SLOT_NIL) goto parse; + if (offset_slot_hint != NULL) + *offset_slot_hint = offset_slot; +offset_slot_access: /* Indexed field */ if (field_map[offset_slot] == 0) return NULL; @@ -478,7 +501,7 @@ tuple_field_raw(struct tuple_format *format, const char *tuple, const uint32_t *field_map, uint32_t field_no) { return tuple_field_raw_by_path(format, tuple, field_map, field_no, - NULL, 0); + NULL, 0, NULL); } /** @@ -513,8 +536,14 @@ static inline const char * tuple_field_by_part_raw(struct tuple_format *format, const char *data, const uint32_t *field_map, struct key_part *part) { + if (unlikely(part->format_epoch != format->epoch)) { + assert(format->epoch != 0); + part->format_epoch = format->epoch; + part->offset_slot_cache = TUPLE_OFFSET_SLOT_NIL; + } return tuple_field_raw_by_path(format, data, field_map, part->fieldno, - part->path, part->path_len); + part->path, part->path_len, + &part->offset_slot_cache); } /**