mirror of https://github.com/postgres/postgres
Tuples can have type RECORDOID and a typmod number that identifies a blessed TupleDesc in a backend-private cache. To support the sharing of such tuples through shared memory and temporary files, provide a typmod registry in shared memory. To achieve that, introduce per-session DSM segments, created on demand when a backend first runs a parallel query. The per-session DSM segment has a table-of-contents just like the per-query DSM segment, and initially the contents are a shared record typmod registry and a DSA area to provide the space it needs to grow. State relating to the current session is accessed via a Session object reached through global variable CurrentSession that may require significant redesign further down the road as we figure out what else needs to be shared or remodelled. Author: Thomas Munro Reviewed-By: Andres Freund Discussion: https://postgr.es/m/CAEepm=0ZtQ-SpsgCyzzYpsXS6e=kZWqk3g5Ygn3MDV7A8dabUA@mail.gmail.compull/31/head^2
parent
9b6cb4650b
commit
cc5f81366c
@ -0,0 +1,208 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* session.c |
||||||
|
* Encapsulation of user session. |
||||||
|
* |
||||||
|
* This is intended to contain data that needs to be shared between backends |
||||||
|
* performing work for a client session. In particular such a session is |
||||||
|
* shared between the leader and worker processes for parallel queries. At |
||||||
|
* some later point it might also become useful infrastructure for separating |
||||||
|
* backends from client connections, e.g. for the purpose of pooling. |
||||||
|
* |
||||||
|
* Currently this infrastructure is used to share: |
||||||
|
* - typemod registry for ephemeral row-types, i.e. BlessTupleDesc etc. |
||||||
|
* |
||||||
|
* Portions Copyright (c) 2017, PostgreSQL Global Development Group |
||||||
|
* |
||||||
|
* src/backend/access/common/session.c |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include "access/session.h" |
||||||
|
#include "storage/lwlock.h" |
||||||
|
#include "storage/shm_toc.h" |
||||||
|
#include "utils/memutils.h" |
||||||
|
#include "utils/typcache.h" |
||||||
|
|
||||||
|
/* Magic number for per-session DSM TOC. */ |
||||||
|
#define SESSION_MAGIC 0xabb0fbc9 |
||||||
|
|
||||||
|
/*
|
||||||
|
* We want to create a DSA area to store shared state that has the same |
||||||
|
* lifetime as a session. So far, it's only used to hold the shared record |
||||||
|
* type registry. We don't want it to have to create any DSM segments just |
||||||
|
* yet in common cases, so we'll give it enough space to hold a very small |
||||||
|
* SharedRecordTypmodRegistry. |
||||||
|
*/ |
||||||
|
#define SESSION_DSA_SIZE 0x30000 |
||||||
|
|
||||||
|
/*
|
||||||
|
* Magic numbers for state sharing in the per-session DSM area. |
||||||
|
*/ |
||||||
|
#define SESSION_KEY_DSA UINT64CONST(0xFFFFFFFFFFFF0001) |
||||||
|
#define SESSION_KEY_RECORD_TYPMOD_REGISTRY UINT64CONST(0xFFFFFFFFFFFF0002) |
||||||
|
|
||||||
|
/* This backend's current session. */ |
||||||
|
Session *CurrentSession = NULL; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up CurrentSession to point to an empty Session object. |
||||||
|
*/ |
||||||
|
void |
||||||
|
InitializeSession(void) |
||||||
|
{ |
||||||
|
CurrentSession = MemoryContextAllocZero(TopMemoryContext, sizeof(Session)); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the per-session DSM segment if it isn't already initialized, and |
||||||
|
* return its handle so that worker processes can attach to it. |
||||||
|
* |
||||||
|
* Unlike the per-context DSM segment, this segement and its contents are |
||||||
|
* reused for future parallel queries. |
||||||
|
* |
||||||
|
* Return DSM_HANDLE_INVALID if a segment can't be allocated due to lack of |
||||||
|
* resources. |
||||||
|
*/ |
||||||
|
dsm_handle |
||||||
|
GetSessionDsmHandle(void) |
||||||
|
{ |
||||||
|
shm_toc_estimator estimator; |
||||||
|
shm_toc *toc; |
||||||
|
dsm_segment *seg; |
||||||
|
size_t typmod_registry_size; |
||||||
|
size_t size; |
||||||
|
void *dsa_space; |
||||||
|
void *typmod_registry_space; |
||||||
|
dsa_area *dsa; |
||||||
|
MemoryContext old_context; |
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have already created a session-scope DSM segment in this backend, |
||||||
|
* return its handle. The same segment will be used for the rest of this |
||||||
|
* backend's lifetime. |
||||||
|
*/ |
||||||
|
if (CurrentSession->segment != NULL) |
||||||
|
return dsm_segment_handle(CurrentSession->segment); |
||||||
|
|
||||||
|
/* Otherwise, prepare to set one up. */ |
||||||
|
old_context = MemoryContextSwitchTo(TopMemoryContext); |
||||||
|
shm_toc_initialize_estimator(&estimator); |
||||||
|
|
||||||
|
/* Estimate space for the per-session DSA area. */ |
||||||
|
shm_toc_estimate_keys(&estimator, 1); |
||||||
|
shm_toc_estimate_chunk(&estimator, SESSION_DSA_SIZE); |
||||||
|
|
||||||
|
/* Estimate space for the per-session record typmod registry. */ |
||||||
|
typmod_registry_size = SharedRecordTypmodRegistryEstimate(); |
||||||
|
shm_toc_estimate_keys(&estimator, 1); |
||||||
|
shm_toc_estimate_chunk(&estimator, typmod_registry_size); |
||||||
|
|
||||||
|
/* Set up segment and TOC. */ |
||||||
|
size = shm_toc_estimate(&estimator); |
||||||
|
seg = dsm_create(size, DSM_CREATE_NULL_IF_MAXSEGMENTS); |
||||||
|
if (seg == NULL) |
||||||
|
{ |
||||||
|
MemoryContextSwitchTo(old_context); |
||||||
|
|
||||||
|
return DSM_HANDLE_INVALID; |
||||||
|
} |
||||||
|
toc = shm_toc_create(SESSION_MAGIC, |
||||||
|
dsm_segment_address(seg), |
||||||
|
size); |
||||||
|
|
||||||
|
/* Create per-session DSA area. */ |
||||||
|
dsa_space = shm_toc_allocate(toc, SESSION_DSA_SIZE); |
||||||
|
dsa = dsa_create_in_place(dsa_space, |
||||||
|
SESSION_DSA_SIZE, |
||||||
|
LWTRANCHE_SESSION_DSA, |
||||||
|
seg); |
||||||
|
shm_toc_insert(toc, SESSION_KEY_DSA, dsa_space); |
||||||
|
|
||||||
|
|
||||||
|
/* Create session-scoped shared record typmod registry. */ |
||||||
|
typmod_registry_space = shm_toc_allocate(toc, typmod_registry_size); |
||||||
|
SharedRecordTypmodRegistryInit((SharedRecordTypmodRegistry *) |
||||||
|
typmod_registry_space, seg, dsa); |
||||||
|
shm_toc_insert(toc, SESSION_KEY_RECORD_TYPMOD_REGISTRY, |
||||||
|
typmod_registry_space); |
||||||
|
|
||||||
|
/*
|
||||||
|
* If we got this far, we can pin the shared memory so it stays mapped for |
||||||
|
* the rest of this backend's life. If we don't make it this far, cleanup |
||||||
|
* callbacks for anything we installed above (ie currently |
||||||
|
* SharedRecordTypemodRegistry) will run when the DSM segment is detached |
||||||
|
* by CurrentResourceOwner so we aren't left with a broken CurrentSession. |
||||||
|
*/ |
||||||
|
dsm_pin_mapping(seg); |
||||||
|
dsa_pin_mapping(dsa); |
||||||
|
|
||||||
|
/* Make segment and area available via CurrentSession. */ |
||||||
|
CurrentSession->segment = seg; |
||||||
|
CurrentSession->area = dsa; |
||||||
|
|
||||||
|
MemoryContextSwitchTo(old_context); |
||||||
|
|
||||||
|
return dsm_segment_handle(seg); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Attach to a per-session DSM segment provided by a parallel leader. |
||||||
|
*/ |
||||||
|
void |
||||||
|
AttachSession(dsm_handle handle) |
||||||
|
{ |
||||||
|
dsm_segment *seg; |
||||||
|
shm_toc *toc; |
||||||
|
void *dsa_space; |
||||||
|
void *typmod_registry_space; |
||||||
|
dsa_area *dsa; |
||||||
|
MemoryContext old_context; |
||||||
|
|
||||||
|
old_context = MemoryContextSwitchTo(TopMemoryContext); |
||||||
|
|
||||||
|
/* Attach to the DSM segment. */ |
||||||
|
seg = dsm_attach(handle); |
||||||
|
if (seg == NULL) |
||||||
|
elog(ERROR, "could not attach to per-session DSM segment"); |
||||||
|
toc = shm_toc_attach(SESSION_MAGIC, dsm_segment_address(seg)); |
||||||
|
|
||||||
|
/* Attach to the DSA area. */ |
||||||
|
dsa_space = shm_toc_lookup(toc, SESSION_KEY_DSA, false); |
||||||
|
dsa = dsa_attach_in_place(dsa_space, seg); |
||||||
|
|
||||||
|
/* Make them available via the current session. */ |
||||||
|
CurrentSession->segment = seg; |
||||||
|
CurrentSession->area = dsa; |
||||||
|
|
||||||
|
/* Attach to the shared record typmod registry. */ |
||||||
|
typmod_registry_space = |
||||||
|
shm_toc_lookup(toc, SESSION_KEY_RECORD_TYPMOD_REGISTRY, false); |
||||||
|
SharedRecordTypmodRegistryAttach((SharedRecordTypmodRegistry *) |
||||||
|
typmod_registry_space); |
||||||
|
|
||||||
|
/* Remain attached until end of backend or DetachSession(). */ |
||||||
|
dsm_pin_mapping(seg); |
||||||
|
dsa_pin_mapping(dsa); |
||||||
|
|
||||||
|
MemoryContextSwitchTo(old_context); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Detach from the current session DSM segment. It's not strictly necessary |
||||||
|
* to do this explicitly since we'll detach automatically at backend exit, but |
||||||
|
* if we ever reuse parallel workers it will become important for workers to |
||||||
|
* detach from one session before attaching to another. Note that this runs |
||||||
|
* detach hooks. |
||||||
|
*/ |
||||||
|
void |
||||||
|
DetachSession(void) |
||||||
|
{ |
||||||
|
/* Runs detach hooks. */ |
||||||
|
dsm_detach(CurrentSession->segment); |
||||||
|
CurrentSession->segment = NULL; |
||||||
|
dsa_detach(CurrentSession->area); |
||||||
|
CurrentSession->area = NULL; |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* session.h |
||||||
|
* Encapsulation of user session. |
||||||
|
* |
||||||
|
* Copyright (c) 2017, PostgreSQL Global Development Group |
||||||
|
* |
||||||
|
* src/include/access/session.h |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#ifndef SESSION_H |
||||||
|
#define SESSION_H |
||||||
|
|
||||||
|
#include "lib/dshash.h" |
||||||
|
|
||||||
|
/* Defined in typcache.c */ |
||||||
|
typedef struct SharedRecordTypmodRegistry SharedRecordTypmodRegistry; |
||||||
|
|
||||||
|
/*
|
||||||
|
* A struct encapsulating some elements of a user's session. For now this |
||||||
|
* manages state that applies to parallel query, but it principle it could |
||||||
|
* include other things that are currently global variables. |
||||||
|
*/ |
||||||
|
typedef struct Session |
||||||
|
{ |
||||||
|
dsm_segment *segment; /* The session-scoped DSM segment. */ |
||||||
|
dsa_area *area; /* The session-scoped DSA area. */ |
||||||
|
|
||||||
|
/* State managed by typcache.c. */ |
||||||
|
SharedRecordTypmodRegistry *shared_typmod_registry; |
||||||
|
dshash_table *shared_record_table; |
||||||
|
dshash_table *shared_typmod_table; |
||||||
|
} Session; |
||||||
|
|
||||||
|
extern void InitializeSession(void); |
||||||
|
extern dsm_handle GetSessionDsmHandle(void); |
||||||
|
extern void AttachSession(dsm_handle handle); |
||||||
|
extern void DetachSession(void); |
||||||
|
|
||||||
|
/* The current session, or NULL for none. */ |
||||||
|
extern Session *CurrentSession; |
||||||
|
|
||||||
|
#endif /* SESSION_H */ |
Loading…
Reference in new issue