Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2d8f56d

Browse files
Rethink create and attach APIs of shared TidStore.
Previously, the behavior of TidStoreCreate() was inconsistent between local and shared TidStore instances in terms of memory limitation. For local TidStore, a memory context was created with initial and maximum memory block sizes, as well as a minimum memory context size, based on the specified max_bytes values. However, for shared TidStore, the provided DSA area was used for TID storage. Although commit bb952c8 allowed specifying the initial and maximum DSA segment sizes, callers would have needed to clamp their own limits, which was not consistent and user-friendly. With this commit, when creating a shared TidStore, a dedicated DSA area is created for TID storage instead of using a provided DSA area. The initial and maximum DSA segment sizes are chosen based on the specified max_bytes. Other processes can attach to the shared TidStore using the handle of the created DSA returned by the new TidStoreGetDSA() function and the DSA pointer returned by TidStoreGetHandle(). The created DSA has the same lifetime as the shared TidStore and is deleted when all processes detach from it. To improve clarity, the TidStoreCreate() function has been divided into two separate functions: TidStoreCreateLocal() and TidStoreCreateShared(). Reviewed-by: John Naylor Discussion: https://postgr.es/m/CAD21AoAyc1j%3DBCdUqZfk6qbdjZ68UgRx1Gkpk0oah4K7S0Ri9g%40mail.gmail.com
1 parent d1cf531 commit 2d8f56d

File tree

3 files changed

+89
-39
lines changed

3 files changed

+89
-39
lines changed

src/backend/access/common/tidstore.c

+82-25
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* Internally it uses a radix tree as the storage for TIDs. The key is the
88
* BlockNumber and the value is a bitmap of offsets, BlocktableEntry.
99
*
10-
* TidStore can be shared among parallel worker processes by passing DSA area
11-
* to TidStoreCreate(). Other backends can attach to the shared TidStore by
12-
* TidStoreAttach().
10+
* TidStore can be shared among parallel worker processes by using
11+
* TidStoreCreateShared(). Other backends can attach to the shared TidStore
12+
* by TidStoreAttach().
1313
*
1414
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
1515
* Portions Copyright (c) 1994, Regents of the University of California
@@ -111,15 +111,16 @@ static void tidstore_iter_extract_tids(TidStoreIter *iter, BlockNumber blkno,
111111
/*
112112
* Create a TidStore. The TidStore will live in the memory context that is
113113
* CurrentMemoryContext at the time of this call. The TID storage, backed
114-
* by a radix tree, will live in its child memory context, rt_context. The
115-
* TidStore will be limited to (approximately) max_bytes total memory
116-
* consumption. If the 'area' is non-NULL, the radix tree is created in the
117-
* DSA area.
114+
* by a radix tree, will live in its child memory context, rt_context.
118115
*
119-
* The returned object is allocated in backend-local memory.
116+
* "max_bytes" is not an internally-enforced limit; it is used only as a
117+
* hint to cap the memory block size of the memory context for TID storage.
118+
* This reduces space wastage due to over-allocation. If the caller wants to
119+
* monitor memory usage, it must compare its limit with the value reported
120+
* by TidStoreMemoryUsage().
120121
*/
121122
TidStore *
122-
TidStoreCreate(size_t max_bytes, dsa_area *area, int tranche_id)
123+
TidStoreCreateLocal(size_t max_bytes)
123124
{
124125
TidStore *ts;
125126
size_t initBlockSize = ALLOCSET_DEFAULT_INITSIZE;
@@ -143,33 +144,74 @@ TidStoreCreate(size_t max_bytes, dsa_area *area, int tranche_id)
143144
initBlockSize,
144145
maxBlockSize);
145146

146-
if (area != NULL)
147-
{
148-
ts->tree.shared = shared_ts_create(ts->rt_context, area,
149-
tranche_id);
150-
ts->area = area;
151-
}
152-
else
153-
ts->tree.local = local_ts_create(ts->rt_context);
147+
ts->tree.local = local_ts_create(ts->rt_context);
154148

155149
return ts;
156150
}
157151

158152
/*
159-
* Attach to the shared TidStore using the given handle. The returned object
160-
* is allocated in backend-local memory using the CurrentMemoryContext.
153+
* Similar to TidStoreCreateLocal() but create a shared TidStore on a
154+
* DSA area. The TID storage will live in the DSA area, and the memory
155+
* context rt_context will have only meta data of the radix tree.
156+
*
157+
* The returned object is allocated in backend-local memory.
161158
*/
162159
TidStore *
163-
TidStoreAttach(dsa_area *area, dsa_pointer handle)
160+
TidStoreCreateShared(size_t max_bytes, int tranche_id)
164161
{
165162
TidStore *ts;
163+
dsa_area *area;
164+
size_t dsa_init_size = DSA_DEFAULT_INIT_SEGMENT_SIZE;
165+
size_t dsa_max_size = DSA_MAX_SEGMENT_SIZE;
166166

167-
Assert(area != NULL);
167+
ts = palloc0(sizeof(TidStore));
168+
ts->context = CurrentMemoryContext;
169+
170+
ts->rt_context = AllocSetContextCreate(CurrentMemoryContext,
171+
"TID storage meta data",
172+
ALLOCSET_SMALL_SIZES);
173+
174+
/*
175+
* Choose the initial and maximum DSA segment sizes to be no longer than
176+
* 1/8 of max_bytes.
177+
*/
178+
while (8 * dsa_max_size > max_bytes)
179+
dsa_max_size >>= 1;
180+
181+
if (dsa_max_size < DSA_MIN_SEGMENT_SIZE)
182+
dsa_max_size = DSA_MIN_SEGMENT_SIZE;
183+
184+
if (dsa_init_size > dsa_max_size)
185+
dsa_init_size = dsa_max_size;
186+
187+
area = dsa_create_ext(tranche_id, dsa_init_size, dsa_max_size);
188+
ts->tree.shared = shared_ts_create(ts->rt_context, area,
189+
tranche_id);
190+
ts->area = area;
191+
192+
return ts;
193+
}
194+
195+
/*
196+
* Attach to the shared TidStore. 'area_handle' is the DSA handle where
197+
* the TidStore is created. 'handle' is the dsa_pointer returned by
198+
* TidStoreGetHandle(). The returned object is allocated in backend-local
199+
* memory using the CurrentMemoryContext.
200+
*/
201+
TidStore *
202+
TidStoreAttach(dsa_handle area_handle, dsa_pointer handle)
203+
{
204+
TidStore *ts;
205+
dsa_area *area;
206+
207+
Assert(area_handle != DSA_HANDLE_INVALID);
168208
Assert(DsaPointerIsValid(handle));
169209

170210
/* create per-backend state */
171211
ts = palloc0(sizeof(TidStore));
172212

213+
area = dsa_attach(area_handle);
214+
173215
/* Find the shared the shared radix tree */
174216
ts->tree.shared = shared_ts_attach(area, handle);
175217
ts->area = area;
@@ -178,17 +220,17 @@ TidStoreAttach(dsa_area *area, dsa_pointer handle)
178220
}
179221

180222
/*
181-
* Detach from a TidStore. This detaches from radix tree and frees the
182-
* backend-local resources. The radix tree will continue to exist until
183-
* it is either explicitly destroyed, or the area that backs it is returned
184-
* to the operating system.
223+
* Detach from a TidStore. This also detaches from radix tree and frees
224+
* the backend-local resources.
185225
*/
186226
void
187227
TidStoreDetach(TidStore *ts)
188228
{
189229
Assert(TidStoreIsShared(ts));
190230

191231
shared_ts_detach(ts->tree.shared);
232+
dsa_detach(ts->area);
233+
192234
pfree(ts);
193235
}
194236

@@ -234,7 +276,11 @@ TidStoreDestroy(TidStore *ts)
234276
{
235277
/* Destroy underlying radix tree */
236278
if (TidStoreIsShared(ts))
279+
{
237280
shared_ts_free(ts->tree.shared);
281+
282+
dsa_detach(ts->area);
283+
}
238284
else
239285
local_ts_free(ts->tree.local);
240286

@@ -420,6 +466,17 @@ TidStoreMemoryUsage(TidStore *ts)
420466
return local_ts_memory_usage(ts->tree.local);
421467
}
422468

469+
/*
470+
* Return the DSA area where the TidStore lives.
471+
*/
472+
dsa_area *
473+
TidStoreGetDSA(TidStore *ts)
474+
{
475+
Assert(TidStoreIsShared(ts));
476+
477+
return ts->area;
478+
}
479+
423480
dsa_pointer
424481
TidStoreGetHandle(TidStore *ts)
425482
{

src/include/access/tidstore.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ typedef struct TidStoreIterResult
2929
OffsetNumber *offsets;
3030
} TidStoreIterResult;
3131

32-
extern TidStore *TidStoreCreate(size_t max_bytes, dsa_area *area,
33-
int tranche_id);
34-
extern TidStore *TidStoreAttach(dsa_area *area, dsa_pointer handle);
32+
extern TidStore *TidStoreCreateLocal(size_t max_bytes);
33+
extern TidStore *TidStoreCreateShared(size_t max_bytes, int tranche_id);
34+
extern TidStore *TidStoreAttach(dsa_handle area_handle, dsa_pointer handle);
3535
extern void TidStoreDetach(TidStore *ts);
3636
extern void TidStoreLockExclusive(TidStore *ts);
3737
extern void TidStoreLockShare(TidStore *ts);
@@ -45,5 +45,6 @@ extern TidStoreIterResult *TidStoreIterateNext(TidStoreIter *iter);
4545
extern void TidStoreEndIterate(TidStoreIter *iter);
4646
extern size_t TidStoreMemoryUsage(TidStore *ts);
4747
extern dsa_pointer TidStoreGetHandle(TidStore *ts);
48+
extern dsa_area *TidStoreGetDSA(TidStore *ts);
4849

4950
#endif /* TIDSTORE_H */

src/test/modules/test_tidstore/test_tidstore.c

+3-11
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ PG_FUNCTION_INFO_V1(test_is_full);
3434
PG_FUNCTION_INFO_V1(test_destroy);
3535

3636
static TidStore *tidstore = NULL;
37-
static dsa_area *dsa = NULL;
3837
static size_t tidstore_empty_size;
3938

4039
/* array for verification of some tests */
@@ -94,7 +93,6 @@ test_create(PG_FUNCTION_ARGS)
9493
size_t array_init_size = 1024;
9594

9695
Assert(tidstore == NULL);
97-
Assert(dsa == NULL);
9896

9997
/*
10098
* Create the TidStore on TopMemoryContext so that the same process use it
@@ -109,18 +107,16 @@ test_create(PG_FUNCTION_ARGS)
109107
tranche_id = LWLockNewTrancheId();
110108
LWLockRegisterTranche(tranche_id, "test_tidstore");
111109

112-
dsa = dsa_create(tranche_id);
110+
tidstore = TidStoreCreateShared(tidstore_max_size, tranche_id);
113111

114112
/*
115113
* Remain attached until end of backend or explicitly detached so that
116114
* the same process use the tidstore for subsequent tests.
117115
*/
118-
dsa_pin_mapping(dsa);
119-
120-
tidstore = TidStoreCreate(tidstore_max_size, dsa, tranche_id);
116+
dsa_pin_mapping(TidStoreGetDSA(tidstore));
121117
}
122118
else
123-
tidstore = TidStoreCreate(tidstore_max_size, NULL, 0);
119+
tidstore = TidStoreCreateLocal(tidstore_max_size);
124120

125121
tidstore_empty_size = TidStoreMemoryUsage(tidstore);
126122

@@ -309,9 +305,5 @@ test_destroy(PG_FUNCTION_ARGS)
309305
pfree(items.lookup_tids);
310306
pfree(items.iter_tids);
311307

312-
if (dsa)
313-
dsa_detach(dsa);
314-
dsa = NULL;
315-
316308
PG_RETURN_VOID();
317309
}

0 commit comments

Comments
 (0)