Coverage Report

Created: 2025-09-04 07:09

/src/tarantool/src/box/space_cache.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: BSD-2-Clause
3
 *
4
 * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
5
 */
6
#include "space_cache.h"
7
8
#include "assoc.h"
9
#include "alter.h"
10
#include "tuple.h"
11
#include "wal_ext.h"
12
13
/** ID -> space dictionary. */
14
static struct mh_i32ptr_t *spaces;
15
/** Name -> space dictionary. */
16
static struct mh_strnptr_t *spaces_by_name;
17
18
/**
19
 * Internal change counter. Grows faster, than public schema_version,
20
 * because we need to remember when to update pointers to already
21
 * non-existent space objects on space:truncate() operation.
22
 */
23
uint32_t space_cache_version;
24
25
/**
26
 * Value of space_cache_version at the time of the last space lookup,
27
 * see space_by_id_fast().
28
 */
29
uint32_t prev_space_cache_version;
30
31
/** Last looked up space, see space_by_id_fast(). */
32
struct space *prev_space;
33
34
const char *space_cache_holder_type_strs[SPACE_HOLDER_MAX] = {
35
  "foreign key",
36
};
37
38
void
39
space_cache_init(void)
40
2
{
41
2
  spaces = mh_i32ptr_new();
42
2
  spaces_by_name = mh_strnptr_new();
43
2
}
44
45
void
46
space_cache_destroy(void)
47
0
{
48
0
  mh_int_t i;
49
0
  mh_foreach(spaces, i) {
50
0
    struct space *space = mh_i32ptr_node(spaces, i)->val;
51
    /** Spaces can be interconnected through constraints. */
52
0
    space_cleanup_constraints(space);
53
0
  }
54
0
  while (mh_size(spaces) > 0) {
55
0
    mh_int_t i = mh_first(spaces);
56
57
0
    struct space *space = (struct space *)
58
0
      mh_i32ptr_node(spaces, i)->val;
59
0
    space_cache_replace(space, NULL);
60
0
    space_delete(space);
61
0
  }
62
0
  mh_i32ptr_delete(spaces);
63
0
  mh_strnptr_delete(spaces_by_name);
64
0
}
65
66
struct space *
67
space_by_id_slow(uint32_t id)
68
16.8k
{
69
16.8k
  mh_int_t space = mh_i32ptr_find(spaces, id, NULL);
70
16.8k
  if (space == mh_end(spaces))
71
16.8k
    return NULL;
72
0
  return (struct space *)mh_i32ptr_node(spaces, space)->val;
73
16.8k
}
74
75
struct space *
76
space_by_name(const char *name, uint32_t len)
77
1.75k
{
78
1.75k
  mh_int_t space = mh_strnptr_find_str(spaces_by_name, name, len);
79
1.75k
  if (space == mh_end(spaces_by_name))
80
1.75k
    return NULL;
81
0
  return (struct space *)mh_strnptr_node(spaces_by_name, space)->val;
82
1.75k
}
83
84
uint32_t
85
space_cache_find_next_unused_id(uint32_t cur_id)
86
0
{
87
0
  for (cur_id++; cur_id <= BOX_SPACE_MAX; cur_id++)
88
0
    if (space_by_id(cur_id) == NULL)
89
0
      break;
90
0
  return cur_id;
91
0
}
92
93
/**
94
 * If the @a old_space space is pinned, relink holders of that space to
95
 * the @a new_space.
96
 */
97
static void
98
space_cache_repin_pinned(struct space *old_space, struct space *new_space)
99
0
{
100
0
  assert(new_space != NULL);
101
0
  if (old_space == NULL)
102
0
    return;
103
104
0
  assert(rlist_empty(&new_space->space_cache_pin_list));
105
0
  rlist_swap(&new_space->space_cache_pin_list,
106
0
       &old_space->space_cache_pin_list);
107
108
0
  struct space_cache_holder *h;
109
0
  rlist_foreach_entry(h, &new_space->space_cache_pin_list, link) {
110
0
    assert(h->space == old_space);
111
0
    h->space = new_space;
112
0
    h->on_replace(h, old_space);
113
0
  }
114
0
}
115
116
void
117
space_cache_replace(struct space *old_space, struct space *new_space)
118
0
{
119
0
  assert(new_space != NULL || old_space != NULL);
120
0
  if (new_space != NULL) {
121
    /*
122
     * If the replaced space has a different name, we
123
     * must explicitly delete it from @spaces_by_name
124
     * cache. Note, since a space id never changes, we
125
     * don't need to do so for @spaces cache.
126
     */
127
0
    struct space *old_space_by_name = NULL;
128
0
    if (old_space != NULL && strcmp(space_name(old_space),
129
0
            space_name(new_space)) != 0) {
130
0
      const char *name = space_name(old_space);
131
0
      mh_int_t k = mh_strnptr_find_str(spaces_by_name, name,
132
0
               strlen(name));
133
0
      assert(k != mh_end(spaces_by_name));
134
0
      old_space_by_name = (struct space *)
135
0
        mh_strnptr_node(spaces_by_name, k)->val;
136
0
      mh_strnptr_del(spaces_by_name, k, NULL);
137
0
    }
138
    /*
139
     * Insert @new_space into @spaces cache, replacing
140
     * @old_space if it's not NULL.
141
     */
142
0
    const struct mh_i32ptr_node_t node_p = { space_id(new_space),
143
0
               new_space };
144
0
    struct mh_i32ptr_node_t old, *p_old = &old;
145
0
    mh_i32ptr_put(spaces, &node_p, &p_old, NULL);
146
0
    struct space *old_space_by_id = p_old != NULL ?
147
0
            (struct space *)p_old->val :
148
0
            NULL;
149
0
    assert(old_space_by_id == old_space);
150
0
    (void)old_space_by_id;
151
    /*
152
     * Insert @new_space into @spaces_by_name cache.
153
     */
154
0
    const char *name = space_name(new_space);
155
0
    uint32_t name_len = strlen(name);
156
0
    uint32_t name_hash = mh_strn_hash(name, name_len);
157
0
    const struct mh_strnptr_node_t node_s = {name, name_len,
158
0
               name_hash, new_space};
159
0
    struct mh_strnptr_node_t old_s, *p_old_s = &old_s;
160
0
    mh_strnptr_put(spaces_by_name, &node_s, &p_old_s, NULL);
161
0
    if (old_space_by_name == NULL && p_old_s != NULL)
162
0
      old_space_by_name = (struct space *)p_old_s->val;
163
0
    assert(old_space_by_name == old_space);
164
0
    (void)old_space_by_name;
165
166
    /* If old space is pinned, we have to pin the new space. */
167
0
    space_cache_repin_pinned(old_space, new_space);
168
    /*
169
     * We should update reference to WAL extensions; otherwise
170
     * since alter operation may yield and then rollback
171
     * (e.g. due to disk issues) - in this gap WAL extensions can
172
     * be reconfigured; as a result space->wal_ext will point to
173
     * dangling (already freed) memory.
174
     */
175
0
    new_space->wal_ext = space_wal_ext_by_name(name);
176
0
  } else {
177
    /*
178
     * Delete @old_space from @spaces cache.
179
     */
180
0
    mh_int_t k = mh_i32ptr_find(spaces, space_id(old_space), NULL);
181
0
    assert(k != mh_end(spaces));
182
0
    struct space *old_space_by_id =
183
0
      (struct space *)mh_i32ptr_node(spaces, k)->val;
184
0
    assert(old_space_by_id == old_space);
185
0
    (void)old_space_by_id;
186
0
    mh_i32ptr_del(spaces, k, NULL);
187
    /*
188
     * Delete @old_space from @spaces_by_name cache.
189
     */
190
0
    const char *name = space_name(old_space);
191
0
    k = mh_strnptr_find_str(spaces_by_name, name, strlen(name));
192
0
    assert(k != mh_end(spaces_by_name));
193
0
    struct space *old_space_by_name =
194
0
      (struct space *)mh_strnptr_node(spaces_by_name, k)->val;
195
0
    assert(old_space_by_name == old_space);
196
0
    (void)old_space_by_name;
197
0
    mh_strnptr_del(spaces_by_name, k, NULL);
198
0
  }
199
0
  space_cache_version++;
200
201
0
  if (trigger_run(&on_alter_space, new_space != NULL ?
202
0
           new_space : old_space) != 0) {
203
0
    diag_log();
204
0
    panic("Can't update space cache");
205
0
  }
206
207
0
  if (old_space != NULL)
208
0
    space_invalidate(old_space);
209
0
}
210
211
void
212
space_cache_on_replace_noop(struct space_cache_holder *holder,
213
          struct space *old_space)
214
0
{
215
0
  (void)holder;
216
0
  (void)old_space;
217
0
}
218
219
void
220
space_cache_pin(struct space *space, struct space_cache_holder *holder,
221
    space_cache_on_replace on_replace,
222
    enum space_cache_holder_type type, bool selfpin)
223
0
{
224
0
  if (!selfpin)
225
0
    assert(mh_i32ptr_find(spaces, space->def->id, NULL)
226
0
           != mh_end(spaces));
227
0
  holder->on_replace = on_replace;
228
0
  holder->type = type;
229
0
  rlist_add_tail(&space->space_cache_pin_list, &holder->link);
230
0
  holder->space = space;
231
0
  holder->selfpin = selfpin;
232
0
}
233
234
void
235
space_cache_unpin(struct space_cache_holder *holder)
236
0
{
237
0
  struct space *space = holder->space; (void)space;
238
0
  if (!holder->selfpin)
239
0
    assert(mh_i32ptr_find(spaces, space->def->id, NULL)
240
0
           != mh_end(spaces));
241
0
#ifndef NDEBUG
242
  /* Paranoid check that the holder in space's pin list. */
243
0
  bool is_in_list = false;
244
0
  struct rlist *tmp;
245
0
  rlist_foreach(tmp, &space->space_cache_pin_list)
246
0
    is_in_list = is_in_list || tmp == &holder->link;
247
0
  assert(is_in_list);
248
0
#endif
249
0
  rlist_del(&holder->link);
250
0
  holder->space = NULL;
251
0
}
252
253
bool
254
space_cache_is_pinned(struct space *space, enum space_cache_holder_type *type)
255
0
{
256
0
  assert(mh_i32ptr_find(spaces, space->def->id, NULL) != mh_end(spaces));
257
0
  struct space_cache_holder *h;
258
0
  rlist_foreach_entry(h, &space->space_cache_pin_list, link) {
259
    /* Self-pinned spaces are treated as not pinned. */
260
0
    if (!h->selfpin) {
261
0
      *type = h->type;
262
0
      return true;
263
0
    }
264
0
  }
265
0
  return false;
266
0
}
267
268
#undef space_by_id
269
270
/** Define the space_by_id() symbol for FFI. */
271
struct space *
272
space_by_id(uint32_t id)
273
0
{
274
0
  return space_by_id_fast(id);
275
0
}