diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index e4a01699e4..858423354e 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1355,6 +1355,36 @@ include_dir 'conf.d' + + shared_dictionaries (integer) + + shared_dictionaries configuration parameter + + + + + Sets the maximum number of text search dictionaries loaded into shared + memory. The default is 10 dictionaries. + + + + Currently controls only loading of Ispell + dictionaries (see ). + After compiling the dictionary it will be copied into shared memory. + Another backends on first use of the dictionary will use it from shared + memory, so it doesn't need to compile the dictionary second time. + DictFile and AffFile are used to + search the dictionary in shared memory. + + + + If the number of simultaneously loaded dictionaries reaches the maximum + allowed number then a new dictionary will be loaded into local memory of + a backend. + + + + huge_pages (enum) diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index 0c86a581c0..c7dce8cac5 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -44,6 +44,7 @@ #include "storage/procsignal.h" #include "storage/sinvaladt.h" #include "storage/spin.h" +#include "tsearch/ts_shared.h" #include "utils/backend_random.h" #include "utils/snapmgr.h" @@ -150,6 +151,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) size = add_size(size, SyncScanShmemSize()); size = add_size(size, AsyncShmemSize()); size = add_size(size, BackendRandomShmemSize()); + size = add_size(size, TsearchShmemSize()); #ifdef EXEC_BACKEND size = add_size(size, ShmemBackendArraySize()); #endif @@ -271,6 +273,11 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) AsyncShmemInit(); BackendRandomShmemInit(); + /* + * Set up shared memory to tsearch + */ + TsearchShmemInit(); + #ifdef EXEC_BACKEND /* diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index 71caac1a1f..2446db7266 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -520,6 +520,7 @@ RegisterLWLockTranches(void) "shared_tuplestore"); LWLockRegisterTranche(LWTRANCHE_TBM, "tbm"); LWLockRegisterTranche(LWTRANCHE_PARALLEL_APPEND, "parallel_append"); + LWLockRegisterTranche(LWTRANCHE_TSEARCH_DSA, "tsearch_dsa"); /* Register named tranches. */ for (i = 0; i < NamedLWLockTrancheRequests; i++) diff --git a/src/backend/tsearch/Makefile b/src/backend/tsearch/Makefile index 227468ae9e..860cd196e9 100644 --- a/src/backend/tsearch/Makefile +++ b/src/backend/tsearch/Makefile @@ -26,7 +26,7 @@ DICTFILES_PATH=$(addprefix dicts/,$(DICTFILES)) OBJS = ts_locale.o ts_parse.o wparser.o wparser_def.o dict.o \ dict_simple.o dict_synonym.o dict_thesaurus.o \ dict_ispell.o regis.o spell.o \ - to_tsany.o ts_selfuncs.o ts_typanalyze.o ts_utils.o + to_tsany.o ts_selfuncs.o ts_shared.o ts_typanalyze.o ts_utils.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/tsearch/ts_shared.c b/src/backend/tsearch/ts_shared.c new file mode 100644 index 0000000000..4682eab506 --- /dev/null +++ b/src/backend/tsearch/ts_shared.c @@ -0,0 +1,179 @@ +/*------------------------------------------------------------------------- + * + * ts_shared.c + * tsearch shared memory management + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/tsearch/ts_shared.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "storage/lwlock.h" +#include "storage/shmem.h" +#include "tsearch/ts_shared.h" + + +/* + * Hash table structures + */ + +typedef struct +{ + char dictfile[MAXPGPATH]; + char afffile[MAXPGPATH]; +} TsearchDictKey; + +typedef struct +{ + TsearchDictKey key; + dsm_handle dict_dsm; +} TsearchDictEntry; + +static HTAB *dict_table; + +/* + * Shared struct for locking + */ +typedef struct +{ + LWLock lock; +} TsearchCtlData; + +static TsearchCtlData *tsearch_ctl; + +/* + * GUC variable for maximum number of shared dictionaries + */ +int shared_dictionaries = 10; + +/* + * Return handle to a dynamic shared memory using hash table. If shared memory + * for dictfile and afffile doesn't allocated yet, do it. + * + * dictbuild: building structure for the dictionary. + * dictfile: .dict file of the dictionary. + * afffile: .aff file of the dictionary. + * allocate_cb: function to build the dictionary, if it wasn't found in DSM. + */ +dsm_handle +ispell_shmem_location(void *dictbuild, + const char *dictfile, const char *afffile, + ispell_build_callback allocate_cb) +{ + TsearchDictKey key; + TsearchDictEntry *entry; + bool found; + dsm_segment *seg; + dsm_handle res; + + StrNCpy(key.dictfile, dictfile, MAXPGPATH); + StrNCpy(key.afffile, afffile, MAXPGPATH); + +refind_entry: + LWLockAcquire(&tsearch_ctl->lock, LW_SHARED); + + entry = (TsearchDictEntry *) hash_search(dict_table, &key, HASH_FIND, + &found); + + /* Dictionary wasn't load into memory */ + if (!found) + { + void *ispell_dict, + *dict_location; + Size ispell_size; + + /* Try to get exclusive lock */ + LWLockRelease(&tsearch_ctl->lock); + if (!LWLockAcquireOrWait(&tsearch_ctl->lock, LW_EXCLUSIVE)) + { + /* + * The lock was released by another backend, try to refind an entry. + */ + goto refind_entry; + } + + entry = (TsearchDictEntry *) hash_search(dict_table, &key, + HASH_ENTER_NULL, + &found); + + /* + * There is no space in shared hash table, let backend to build the + * dictionary within its memory context. + */ + if (entry == NULL) + return DSM_HANDLE_INVALID; + + /* The lock was free so add new entry */ + ispell_dict = allocate_cb(dictbuild, dictfile, afffile, &ispell_size); + + seg = dsm_create(ispell_size, 0); + dict_location = dsm_segment_address(seg); + memcpy(dict_location, ispell_dict, ispell_size); + + pfree(ispell_dict); + + entry->dict_dsm = dsm_segment_handle(seg); + res = entry->dict_dsm; + + /* Remain attached until end of postmaster */ + dsm_pin_segment(seg); + + dsm_detach(seg); + } + else + { + res = entry->dict_dsm; + } + + LWLockRelease(&tsearch_ctl->lock); + + return res; +} + +/* + * Allocate and initialize tsearch-related shared memory. + */ +void +TsearchShmemInit(void) +{ + HASHCTL ctl; + bool found; + + tsearch_ctl = (TsearchCtlData *) + ShmemInitStruct("Full Text Search Ctl", sizeof(TsearchCtlData), &found); + + if (!found) + LWLockInitialize(&tsearch_ctl->lock, LWTRANCHE_TSEARCH_DSA); + + memset(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(TsearchDictKey); + ctl.entrysize = sizeof(TsearchDictEntry); + + dict_table = ShmemInitHash("Shared Tsearch Lookup Table", + shared_dictionaries, shared_dictionaries, + &ctl, + HASH_ELEM | HASH_BLOBS); +} + +/* + * Report shared memory space needed by TsearchShmemInit. + */ +Size +TsearchShmemSize(void) +{ + Size size = 0; + + /* size of service structure */ + size = add_size(size, MAXALIGN(sizeof(TsearchCtlData))); + + /* size of lookup hash table */ + size = add_size(size, hash_estimate_size(shared_dictionaries, + sizeof(TsearchDictEntry))); + + return size; +} diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 72f6be329e..dbc9bf93f0 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -76,6 +76,7 @@ #include "storage/predicate.h" #include "tcop/tcopprot.h" #include "tsearch/ts_cache.h" +#include "tsearch/ts_shared.h" #include "utils/builtins.h" #include "utils/bytea.h" #include "utils/guc_tables.h" @@ -2910,6 +2911,19 @@ static struct config_int ConfigureNamesInt[] = NULL, NULL, NULL }, + { + {"shared_dictionaries", PGC_POSTMASTER, RESOURCES_MEM, + gettext_noop("Sets the maximum number of text search dictionaries loaded into shared memory."), + gettext_noop("Currently controls only loading of Ispell dictionaries. " + "If the number of simultaneously loaded dictionaries " + "reaches the maximum allowed number then a new dictionary " + "will be loaded into local memory of a backend.") + }, + &shared_dictionaries, + 10, 0, INT_MAX, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 69f40f04b0..b83ffe6a39 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -131,6 +131,7 @@ # mmap # use none to disable dynamic shared memory # (change requires restart) +#shared_dictionaries = 10 # (change requires restart) # - Disk - diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index c21bfe2f66..2bb80cdd26 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -219,6 +219,7 @@ typedef enum BuiltinTrancheIds LWTRANCHE_SHARED_TUPLESTORE, LWTRANCHE_TBM, LWTRANCHE_PARALLEL_APPEND, + LWTRANCHE_TSEARCH_DSA, LWTRANCHE_FIRST_USER_DEFINED } BuiltinTrancheIds; diff --git a/src/include/tsearch/ts_shared.h b/src/include/tsearch/ts_shared.h new file mode 100644 index 0000000000..4bcfb437ef --- /dev/null +++ b/src/include/tsearch/ts_shared.h @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + * + * ts_shared.h + * tsearch shared memory management + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * + * src/include/tsearch/ts_shared.h + * + *------------------------------------------------------------------------- + */ +#ifndef TS_SHARED_H +#define TS_SHARED_H + +#include "c.h" +#include "storage/dsm.h" + +/* + * GUC variable for maximum number of shared dictionaries + */ +extern int shared_dictionaries; + +typedef void *(*ispell_build_callback) (void *dictbuild, + const char *dictfile, + const char *afffile, + Size *size); + +extern dsm_handle ispell_shmem_location(void *dictbuild, + const char *dictfile, const char *afffile, + ispell_build_callback allocate_cb); + +extern void TsearchShmemInit(void); +extern Size TsearchShmemSize(void); + +#endif /* TS_SHARED_H */