Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * spccache.c
4 : * Tablespace cache management.
5 : *
6 : * We cache the parsed version of spcoptions for each tablespace to avoid
7 : * needing to reparse on every lookup. Right now, there doesn't appear to
8 : * be a measurable performance gain from doing this, but that might change
9 : * in the future as we add more options.
10 : *
11 : * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
12 : * Portions Copyright (c) 1994, Regents of the University of California
13 : *
14 : * IDENTIFICATION
15 : * src/backend/utils/cache/spccache.c
16 : *
17 : *-------------------------------------------------------------------------
18 : */
19 : #include "postgres.h"
20 :
21 : #include "access/reloptions.h"
22 : #include "catalog/pg_tablespace.h"
23 : #include "commands/tablespace.h"
24 : #include "miscadmin.h"
25 : #include "optimizer/optimizer.h"
26 : #include "storage/bufmgr.h"
27 : #include "utils/catcache.h"
28 : #include "utils/hsearch.h"
29 : #include "utils/inval.h"
30 : #include "utils/spccache.h"
31 : #include "utils/syscache.h"
32 :
33 :
34 : /* Hash table for information about each tablespace */
35 : static HTAB *TableSpaceCacheHash = NULL;
36 :
37 : typedef struct
38 : {
39 : Oid oid; /* lookup key - must be first */
40 : TableSpaceOpts *opts; /* options, or NULL if none */
41 : } TableSpaceCacheEntry;
42 :
43 :
44 : /*
45 : * InvalidateTableSpaceCacheCallback
46 : * Flush all cache entries when pg_tablespace is updated.
47 : *
48 : * When pg_tablespace is updated, we must flush the cache entry at least
49 : * for that tablespace. Currently, we just flush them all. This is quick
50 : * and easy and doesn't cost much, since there shouldn't be terribly many
51 : * tablespaces, nor do we expect them to be frequently modified.
52 : */
53 : static void
54 252 : InvalidateTableSpaceCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
55 : {
56 : HASH_SEQ_STATUS status;
57 : TableSpaceCacheEntry *spc;
58 :
59 252 : hash_seq_init(&status, TableSpaceCacheHash);
60 372 : while ((spc = (TableSpaceCacheEntry *) hash_seq_search(&status)) != NULL)
61 : {
62 120 : if (spc->opts)
63 4 : pfree(spc->opts);
64 120 : if (hash_search(TableSpaceCacheHash,
65 120 : (void *) &spc->oid,
66 : HASH_REMOVE,
67 : NULL) == NULL)
68 0 : elog(ERROR, "hash table corrupted");
69 : }
70 252 : }
71 :
72 : /*
73 : * InitializeTableSpaceCache
74 : * Initialize the tablespace cache.
75 : */
76 : static void
77 4070 : InitializeTableSpaceCache(void)
78 : {
79 : HASHCTL ctl;
80 :
81 : /* Initialize the hash table. */
82 4070 : ctl.keysize = sizeof(Oid);
83 4070 : ctl.entrysize = sizeof(TableSpaceCacheEntry);
84 4070 : TableSpaceCacheHash =
85 4070 : hash_create("TableSpace cache", 16, &ctl,
86 : HASH_ELEM | HASH_BLOBS);
87 :
88 : /* Make sure we've initialized CacheMemoryContext. */
89 4070 : if (!CacheMemoryContext)
90 0 : CreateCacheMemoryContext();
91 :
92 : /* Watch for invalidation events. */
93 4070 : CacheRegisterSyscacheCallback(TABLESPACEOID,
94 : InvalidateTableSpaceCacheCallback,
95 : (Datum) 0);
96 4070 : }
97 :
98 : /*
99 : * get_tablespace
100 : * Fetch TableSpaceCacheEntry structure for a specified table OID.
101 : *
102 : * Pointers returned by this function should not be stored, since a cache
103 : * flush will invalidate them.
104 : */
105 : static TableSpaceCacheEntry *
106 1363118 : get_tablespace(Oid spcid)
107 : {
108 : TableSpaceCacheEntry *spc;
109 : HeapTuple tp;
110 : TableSpaceOpts *opts;
111 :
112 : /*
113 : * Since spcid is always from a pg_class tuple, InvalidOid implies the
114 : * default.
115 : */
116 1363118 : if (spcid == InvalidOid)
117 1288516 : spcid = MyDatabaseTableSpace;
118 :
119 : /* Find existing cache entry, if any. */
120 1363118 : if (!TableSpaceCacheHash)
121 4070 : InitializeTableSpaceCache();
122 1363118 : spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
123 : (void *) &spcid,
124 : HASH_FIND,
125 : NULL);
126 1363118 : if (spc)
127 1358138 : return spc;
128 :
129 : /*
130 : * Not found in TableSpace cache. Check catcache. If we don't find a
131 : * valid HeapTuple, it must mean someone has managed to request tablespace
132 : * details for a non-existent tablespace. We'll just treat that case as
133 : * if no options were specified.
134 : */
135 4980 : tp = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spcid));
136 4980 : if (!HeapTupleIsValid(tp))
137 0 : opts = NULL;
138 : else
139 : {
140 : Datum datum;
141 : bool isNull;
142 :
143 4980 : datum = SysCacheGetAttr(TABLESPACEOID,
144 : tp,
145 : Anum_pg_tablespace_spcoptions,
146 : &isNull);
147 4980 : if (isNull)
148 4976 : opts = NULL;
149 : else
150 : {
151 4 : bytea *bytea_opts = tablespace_reloptions(datum, false);
152 :
153 4 : opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
154 4 : memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
155 : }
156 4980 : ReleaseSysCache(tp);
157 : }
158 :
159 : /*
160 : * Now create the cache entry. It's important to do this only after
161 : * reading the pg_tablespace entry, since doing so could cause a cache
162 : * flush.
163 : */
164 4980 : spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash,
165 : (void *) &spcid,
166 : HASH_ENTER,
167 : NULL);
168 4980 : spc->opts = opts;
169 4980 : return spc;
170 : }
171 :
172 : /*
173 : * get_tablespace_page_costs
174 : * Return random and/or sequential page costs for a given tablespace.
175 : *
176 : * This value is not locked by the transaction, so this value may
177 : * be changed while a SELECT that has used these values for planning
178 : * is still executing.
179 : */
180 : void
181 1349090 : get_tablespace_page_costs(Oid spcid,
182 : double *spc_random_page_cost,
183 : double *spc_seq_page_cost)
184 : {
185 1349090 : TableSpaceCacheEntry *spc = get_tablespace(spcid);
186 :
187 : Assert(spc != NULL);
188 :
189 1349090 : if (spc_random_page_cost)
190 : {
191 1095534 : if (!spc->opts || spc->opts->random_page_cost < 0)
192 1095534 : *spc_random_page_cost = random_page_cost;
193 : else
194 0 : *spc_random_page_cost = spc->opts->random_page_cost;
195 : }
196 :
197 1349090 : if (spc_seq_page_cost)
198 : {
199 947354 : if (!spc->opts || spc->opts->seq_page_cost < 0)
200 947218 : *spc_seq_page_cost = seq_page_cost;
201 : else
202 136 : *spc_seq_page_cost = spc->opts->seq_page_cost;
203 : }
204 1349090 : }
205 :
206 : /*
207 : * get_tablespace_io_concurrency
208 : *
209 : * This value is not locked by the transaction, so this value may
210 : * be changed while a SELECT that has used these values for planning
211 : * is still executing.
212 : */
213 : int
214 12148 : get_tablespace_io_concurrency(Oid spcid)
215 : {
216 12148 : TableSpaceCacheEntry *spc = get_tablespace(spcid);
217 :
218 12148 : if (!spc->opts || spc->opts->effective_io_concurrency < 0)
219 12148 : return effective_io_concurrency;
220 : else
221 0 : return spc->opts->effective_io_concurrency;
222 : }
223 :
224 : /*
225 : * get_tablespace_maintenance_io_concurrency
226 : */
227 : int
228 1880 : get_tablespace_maintenance_io_concurrency(Oid spcid)
229 : {
230 1880 : TableSpaceCacheEntry *spc = get_tablespace(spcid);
231 :
232 1880 : if (!spc->opts || spc->opts->maintenance_io_concurrency < 0)
233 1880 : return maintenance_io_concurrency;
234 : else
235 0 : return spc->opts->maintenance_io_concurrency;
236 : }
|