Skip to content

Put uuids in the vae and eav index#1326

Merged
dwwoelfel merged 7 commits intomainfrom
vae-uuids
Jul 2, 2025
Merged

Put uuids in the vae and eav index#1326
dwwoelfel merged 7 commits intomainfrom
vae-uuids

Conversation

@dwwoelfel
Copy link
Contributor

We only put refs in the vae and eav indexes so we know that the value will be a uuid. This PR stores those uuids as actual uuids in the index itself.

This makes the indexes take up less space (they're about 30% smaller).

The big change is that now we can use vae instead of eav when we do a join on a ref, which saves us doing an expensive json->uuid conversion in the join filter. Here's a simple example:

{:books {:where {:category "western" :author :eid/elmer-kelton}}}

That generates two ctes:

with m_0 as (
  select entity_id as m_0_eid from triples t0 where attr_id = :category-eid and value = "western"
),
m_1 as (
  select m_0.*, entity_id as m_1_eid from triples t1 where value = :eid/elmer-kelton and m_0.m_0_eid = json_to_uuid(value)
)

If we use the eav index for m_1, the v is at the end of the index. Postgres does a nested loop and filters the join on entity_id = json_to_uuid(value). It looks something like this:

    ->  Nested Loop
          Join Filter: (t0.entity_id = ((t1.value ->> 0))::uuid)
          Rows Removed by Join Filter: N
          ->  Index Scan using ave_index on triples t0
                Index Cond: (attr_id = :category-eid) AND (value = '"western"'::jsonb)
          ->  Index Scan using eav_uuid_index on triples t1
                Index Cond: ((entity_id = :eid) AND (attr_id = :author-eid))

If we use the vae index for m_1, then the v is at the start of the index and postgres will use that in the index cond instead of the less efficient join filter. It looks something like this:

    ->  Nested Loop 
          ->  Index Scan using ave_index on triples t0 
                Index Cond: (attr_id = :category-eid) AND (value = '"western"'::jsonb)
          ->  Index Scan using vae_uuid_index on triples t1
                Index Cond: ((((value ->> 0))::uuid = t0.entity_id) AND (attr_id = :author-eid) AND (entity_id =:eid))

((value ->> 0))::uuid is already in the index, so we never have to do any json->uuid conversion at all in the query itself. We avoid the expensive join filter, which in my backtesting makes queries much faster.

We also don't have to carry around the match_x_x_value_uuid any more. Instead of value_uuid, we track is_ref_value so that we know to convert it from a string to a uuid in sql-row->triple.

Results from the backtest:

I'm not super concerned about the first two queries that got worse because they're not slower if I enable pg hints.

:old-explain :new-explain :improvement
513 2075 -1562
676 1578 -901
393 574 -181
489 595 -105
286 375 -89
3 86 -83
6 87 -80
4 83 -78
5 84 -78
5 84 -78
6 84 -78
341 418 -76
5 82 -76
5 79 -74
306 379 -73
5 70 -65
99 164 -64
290 351 -61
110 171 -60
280 340 -60
117 175 -58
134 192 -58
117 173 -56
80 135 -55
4 59 -55
144 198 -54
5 58 -53
4 58 -53
110 163 -53
125 177 -52
4 56 -51
115 166 -51
131 181 -49
238 288 -49
156 205 -49
139 188 -49
4 53 -49
87 136 -48
445 494 -48
4 53 -48
457 502 -45
136 181 -44
110 153 -43
187 230 -43
134 177 -42
100 143 -42
112 154 -41
58 99 -41
22 63 -41
274 316 -41
119 160 -41
22 63 -41
87 128 -40
134 173 -38
120 158 -38
20 58 -38
379 417 -38
129 167 -38
103 139 -36
112 148 -35
125 159 -34
18 52 -33
113 147 -33
103 136 -33
109 141 -32
82 113 -31
170 201 -30
90 121 -30
196 226 -30
113 141 -28
73 100 -27
96 123 -27
157 184 -26
79 105 -26
205 231 -25
7 33 -25
29 54 -24
277 302 -24
82 106 -24
74 97 -23
278 302 -23
155 178 -22
350 371 -21
8 29 -20
299 320 -20
17 38 -20
76 97 -20
72 93 -20
10 30 -19
149 167 -18
20 38 -18
66 84 -18
19 37 -18
246 264 -17
42 59 -17
96 113 -17
70 88 -17
82 100 -17
36 53 -16
29 45 -15
6 21 -15
4 19 -15
3 18 -15
9 24 -15
38 53 -15
120 135 -15
61 76 -15
3 18 -14
54 69 -14
98 113 -14
93 108 -14
113 127 -14
3 17 -14
3 17 -13
7 20 -12
42 55 -12
315 328 -12
220 232 -11
4 15 -11
10 21 -11
37 47 -10
39 49 -10
28 38 -9
44 54 -9
21 31 -9
43 53 -9
10 20 -9
33 42 -9
26 35 -9
30 39 -8
72 81 -8
47 55 -8
50 58 -8
53 61 -7
4 12 -7
44 51 -7
52 59 -6
37 44 -6
24 31 -6
56 63 -6
97 103 -6
57 64 -6
80 87 -6
48 54 -5
128 134 -5
51 57 -5
3 8 -5
40 45 -5
3 8 -4
120 125 -4
28 33 -4
50 55 -4
129 133 -4
2 6 -4
20 24 -4
21 25 -4
20 24 -4
12 16 -3
40 44 -3
47 51 -3
174 178 -3
19 22 -3
9 12 -3
17 20 -3
27 31 -3
17 21 -3
9 12 -3
18 21 -3
12 15 -3
41 44 -3
132 135 -3
19 22 -2
30 33 -2
9 12 -2
36 38 -2
18 21 -2
9 11 -2
13 15 -2
8 10 -2
4 6 -1
11 13 -1
44 45 -1
29 31 -1
10 12 -1
14 15 -1
2 4 -1
99 101 -1
15 16 -1
10 12 -1
40 41 -1
14 15 -1
27 28 -1
73 74 -1
6 8 -1
8 9 -1
49 50 -1
15 16 -1
20 21 -1
13 14 -1
12 13 -1
6 7 -1
6 7 -1
4 5 -1
22 23 -1
33 34 0
6 7 0
14 15 0
7 8 0
1 2 0
16 16 0
57 58 0
6 6 0
14 15 0
11 12 0
7 8 0
1 1 0
2 2 0
2 2 0
4 5 0
18 19 0
16 16 0
1 2 0
4 5 0
19 20 0
10 11 0
2 2 0
2 2 0
4 5 0
21 22 0
4 4 0
3 3 0
2 3 0
8 8 0
8 8 0
1 1 0
30 31 0
6 7 0
23 23 0
7 8 0
4 4 0
7 8 0
6 7 0
1 1 0
2 2 0
4 5 0
8 8 0
18 18 0
1 1 0
1 1 0
2 2 0
1 1 0
29 29 0
1 1 0
1 1 0
2 2 0
10 10 0
4 4 0
2 2 0
3 4 0
1 1 0
1 1 0
1 1 0
1 1 0
12 12 0
1 1 0
1 2 0
1 1 0
13 13 0
8 8 0
1 2 0
3 4 0
0 1 0
0 0 0
1 1 0
15 15 0
28 28 0
1 1 0
9 10 0
18 18 0
2 2 0
2 2 0
1 1 0
2 2 0
1 1 0
30000 30000 0
30000 30000 0
30000 30000 0
1 1 0
5 5 0
2 2 0
56 56 0
5 5 0
2 2 0
1 1 0
1 1 0
9 9 0
1 1 0
2 2 0
7 7 0
2 2 0
27 27 0
3 2 0
1 1 0
7 7 0
3 3 0
5 5 0
5 5 0
14 14 0
6 6 0
10 10 0
2 1 0
34 34 0
23 23 0
2 2 0
4 3 0
7 7 0
1 1 0
17 16 0
2 1 0
4 3 0
5 4 0
6 6 0
1 1 0
16 15 0
3 2 0
13 13 0
2 1 0
2 2 0
1 1 0
10 10 0
1 1 0
9 9 0
4 3 0
5 5 0
2 1 0
7 7 0
5 5 0
2 2 0
3 3 0
7 7 0
3 3 0
8 7 0
1 1 0
7 7 0
3 2 0
8 8 0
7 6 0
6 5 0
22 21 0
7 7 0
14 14 0
14 13 0
7 7 0
3 2 0
7 7 0
4 3 0
53 52 0
6 5 0
9 8 0
13 12 0
2 1 0
21 21 0
7 6 0
8 7 0
17 17 0
9 8 0
3 2 0
5 4 0
19 18 0
7 6 0
19 18 0
4 4 0
3 2 0
2 1 0
5 4 0
14 13 0
8 7 0
11 10 0
3 2 0
11 10 0
27 26 0
40 39 0
3 2 0
3 2 0
2 1 0
2 1 1
6 5 1
4 3 1
6 5 1
3 2 1
5 4 1
3 2 1
4 3 1
4 3 1
37 35 1
10 8 1
4 2 1
3 2 1
8 6 1
6 5 1
4 3 1
7 6 1
5 4 1
31 30 1
10 8 1
6 5 1
62 61 1
4 3 1
5 3 1
13 12 1
28 27 1
9 7 1
4 2 1
4 3 1
9 8 1
3 2 1
9 7 1
11 10 1
51 50 1
5 3 1
58 57 1
11 10 1
8 6 1
15 14 1
7 5 1
23 22 1
10 9 1
19 18 1
4 3 1
99 98 1
6 5 1
8 7 1
6 4 1
12 10 1
4 2 1
28 26 1
24 23 1
47 46 1
12 10 1
15 13 1
4 2 1
6 4 1
6 4 1
27 25 2
4 2 2
20 18 2
10 8 2
24 22 2
5 2 2
9 7 2
6 4 2
17 15 2
13 11 2
32 30 2
11 8 2
16 13 2
18 16 2
8 6 2
13 11 2
19 16 2
25 22 2
566 563 2
14 11 2
10 7 2
20 17 2
61 58 2
11 8 2
23 20 2
17 14 2
21 18 2
12 9 3
25 22 3
16 13 3
22 19 3
41 37 3
9 6 3
38 34 3
12 8 3
38 34 3
25 21 3
16 12 3
38 35 3
57 54 3
23 19 3
9 5 3
20 16 4
12 8 4
45 41 4
56 52 4
13 8 4
24 19 4
36 31 4
9 5 4
23 19 4
56 51 4
22 17 4
62 57 4
30 26 4
22 17 4
20 15 4
91 86 4
14 9 4
74 69 4
39 34 4
43 38 4
50 45 4
131 126 4
12 7 5
19 14 5
31 26 5
22 17 5
26 21 5
47 41 5
20 15 5
188 182 5
44 39 5
39 34 5
47 41 6
70 64 6
55 49 6
46 40 6
70 63 6
52 45 6
44 38 6
45 38 6
62 55 6
28 21 7
42 35 7
16 9 7
68 61 7
57 50 7
13 6 7
47 40 7
58 50 7
46 38 7
37 29 8
45 37 8
43 34 8
34 26 8
43 34 9
16 7 9
57 48 9
16 7 9
36 26 9
166 156 9
17 7 9
46 36 9
18 8 10
57 47 10
56 45 11
143 132 11
41 30 11
40 28 11
25 12 12
67 55 12
43 30 12
55 42 12
25 13 12
72 59 12
42 29 12
23 10 12
27 14 13
131 117 13
39 26 13
23 9 13
43 29 13
51 37 14
176 161 14
89 73 15
22 6 15
114 98 15
27 11 15
40 24 15
30 14 16
214 198 16
84 67 16
75 58 17
85 66 18
24 5 18
31 12 18
88 69 19
134 115 19
220 200 19
40 20 20
48 27 20
62 41 20
76 55 21
130 109 21
33 12 21
84 63 21
103 81 21
193 171 22
105 82 22
75 52 22
41 19 22
101 78 22
100 77 22
55 32 23
81 58 23
216 193 23
97 73 23
134 110 23
77 53 23
150 126 23
54 30 24
146 121 24
160 134 25
113 87 26
47 19 27
53 24 29
121 91 30
209 179 30
196 165 30
62 31 30
59 29 30
389 358 31
102 69 33
105 72 33
81 48 33
73 38 34
228 190 37
202 162 39
81 41 40
81 41 40
107 66 41
500 459 41
84 42 41
330 287 42
134 91 42
146 101 44
127 82 44
87 42 45
85 39 45
401 356 45
347 301 46
704 658 46
599 553 46
251 204 46
78 31 46
287 240 47
97 49 47
134 87 47
89 41 47
253 205 48
117 69 48
99 50 48
426 377 48
355 306 49
213 164 49
116 66 50
228 177 50
97 46 51
205 153 51
70 18 51
124 73 51
95 43 52
126 73 52
95 42 52
97 44 53
179 125 53
311 257 54
340 285 54
142 87 54
224 169 55
307 249 57
100 42 58
82 22 59
432 372 59
146 85 60
108 47 61
107 41 65
329 263 66
162 94 68
318 249 68
356 287 69
236 166 69
238 167 70
361 290 70
148 77 71
447 372 74
234 158 76
245 168 77
428 349 78
598 519 79
128 45 82
181 96 85
247 161 86
230 141 89
140 48 92
160 67 92
365 269 96
378 281 96
324 224 99
123 23 100
288 187 101
169 65 103
206 100 105
308 202 106
876 767 109
566 456 110
225 114 111
197 85 111
359 246 113
159 44 114
240 125 115
377 256 120
344 220 123
234 110 124
240 116 124
215 89 125
212 85 126
207 79 127
208 80 128
211 83 128
315 184 131
211 80 131
171 38 132
171 38 133
214 79 135
173 37 135
437 301 135
185 49 136
219 82 137
216 78 138
251 111 140
543 402 140
179 37 141
215 73 142
227 84 142
234 88 145
228 81 147
694 545 148
411 262 149
239 87 152
237 82 155
240 85 155
244 81 163
403 238 164
812 638 173
665 491 174
597 407 190
321 120 200
721 508 212
280 54 226
476 239 237
353 115 238
328 87 240
1463 1204 258
724 455 268
701 398 302
526 159 367
1294 924 369
496 122 374
462 86 376
1313 927 386
590 196 393
1347 945 402
1339 907 432
595 144 451
674 220 454
1435 942 492
1278 781 496
740 221 518
696 174 522
742 189 552
919 227 691
1039 265 773
1068 285 782
1090 287 802
1090 283 807
1311 473 838
1175 334 840
1129 282 847
1163 295 867
1246 340 906
2019 460 1558
2467 868 1599
2404 717 1686
3170 1395 1774
3178 1291 1886
2624 736 1887
4255 2099 2155
2907 710 2196
2828 574 2253
3258 738 2519
3374 730 2644
4362 1633 2728
4320 1567 2752
4321 1505 2815
4376 1556 2819
4451 1620 2830
4405 1551 2853
4421 1567 2853
4366 1469 2896
4468 1550 2918
4458 1532 2925
4478 1490 2987
4550 1547 3002
4550 1540 3009
4644 1611 3033
4547 1510 3037
4675 1606 3069
4725 1641 3084
4718 1630 3087
4685 1593 3091
4733 1618 3115
4645 1523 3121
4629 1507 3122
4780 1653 3126
4716 1581 3135
4746 1601 3145
4998 1851 3146
4697 1549 3147
4679 1526 3152
4763 1605 3158
4708 1531 3177
4700 1512 3188
4759 1569 3189
4723 1533 3189
4672 1479 3193
4826 1627 3198
4774 1574 3199
4685 1482 3202
4748 1540 3207
4833 1621 3211
4710 1496 3213
4725 1506 3219
4745 1520 3224
4752 1523 3229
4771 1537 3233
4815 1578 3237
4799 1561 3238
4780 1531 3249
4743 1490 3252
4734 1481 3252
4788 1533 3255
4794 1524 3269
4939 1660 3279
4860 1572 3287
4832 1534 3297
4768 1467 3300
4795 1461 3333
4918 1549 3368
4968 1551 3417
5006 1451 3555
4121 247 3874
5340 1347 3993
6845 1556 5289
10090 2132 7958

Deployment plan

  1. Merge into main
  2. Add the indexes concurrently:
create or replace function public.json_uuid_to_uuid(v jsonb) returns uuid
  language sql
  parallel safe
  immutable
  as $$
   select (v->>0)::uuid
  end;
$$;

create index concurrently if not exists vae_uuid_index
  on triples(app_id, public.json_uuid_to_uuid(value), attr_id, entity_id)
  where vae;

create unique index concurrently if not exists eav_uuid_index
  on triples(app_id, entity_id, attr_id, public.json_uuid_to_uuid(value))
  where eav;
  1. Run the migration
  2. Deploy the code
  3. Drop ave_index and eav_index in a followup PR (which also needs to happen concurrently)

@github-actions
Copy link
Contributor

github-actions bot commented Jul 2, 2025

View Vercel preview at instant-www-js-vae-uuids-jsv.vercel.app.

(assoc acc pat-idx {:sym sym
:ref-value? (and (= :v component)
(= :eav (idx-key (:idx named-p))))})
(= :vae (idx-key (:idx named-p))))})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also fixes a bug in the topics, where we weren't removing vae triples from the topics symbol map. (see https://github.com/jsventures/instant/pull/827 for more explanation).

I don't think we actually encounter the bug in prod because we always invalidate the id field on updates (will be fixed by #739). What could happen when we fix the id bug: say you have a query that orders on order-field, if you update order-field on an existing entity outside of the result set so that it should be in the result set, we won't invalidate the query because our topic is looking at a specific entity (e.g. [:eid :order-field _]). With this change, the topic will be [_ :order-field :_], so the query will get invalidated. Again, it's not a problem now because we have the topic [_ :id _] and it gets invalidated on any change.

end;
$$;

-- Create this index concurrently before running the migration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

patterns)]
(collect-query-results ctx (:data datalog-result) forms)))))

(defn explain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh heck yeah!

Copy link
Contributor

@stopachka stopachka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Genius find @dwwoelfel!

@dwwoelfel dwwoelfel merged commit 25720e9 into main Jul 2, 2025
33 checks passed
@dwwoelfel dwwoelfel deleted the vae-uuids branch July 2, 2025 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants