mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	Simplify autovacuum work-item implementation
The initial implementation of autovacuum work-items used a dynamic shared memory area (DSA). However, it's argued that dynamic shared memory is not portable enough, so we cannot rely on it being supported everywhere; at the same time, autovacuum work-items are now a critical part of the server, so it's not acceptable that they don't work in the cases where dynamic shared memory is disabled. Therefore, let's fall back to a simpler implementation of work-items that just uses autovacuum's main shared memory segment for storage. Discussion: https://postgr.es/m/CA+TgmobQVbz4K_+RSmiM9HeRKpy3vS5xnbkL95gSEnWijzprKQ@mail.gmail.com
This commit is contained in:
		
							parent
							
								
									b73f1b5c29
								
							
						
					
					
						commit
						31ae1638ce
					
				| @ -245,6 +245,24 @@ typedef enum | ||||
| 	AutoVacNumSignals			/* must be last */ | ||||
| }			AutoVacuumSignal; | ||||
| 
 | ||||
| /*
 | ||||
|  * Autovacuum workitem array, stored in AutoVacuumShmem->av_workItems.  This | ||||
|  * list is mostly protected by AutovacuumLock, except that if an item is | ||||
|  * marked 'active' other processes must not modify the work-identifying | ||||
|  * members. | ||||
|  */ | ||||
| typedef struct AutoVacuumWorkItem | ||||
| { | ||||
| 	AutoVacuumWorkItemType avw_type; | ||||
| 	bool		avw_used;		/* below data is valid */ | ||||
| 	bool		avw_active;		/* being processed */ | ||||
| 	Oid			avw_database; | ||||
| 	Oid			avw_relation; | ||||
| 	BlockNumber avw_blockNumber; | ||||
| } AutoVacuumWorkItem; | ||||
| 
 | ||||
| #define NUM_WORKITEMS	256 | ||||
| 
 | ||||
| /*-------------
 | ||||
|  * The main autovacuum shmem struct.  On shared memory we store this main | ||||
|  * struct and the array of WorkerInfo structs.  This struct keeps: | ||||
| @ -255,10 +273,10 @@ typedef enum | ||||
|  * av_runningWorkers the WorkerInfo non-free queue | ||||
|  * av_startingWorker pointer to WorkerInfo currently being started (cleared by | ||||
|  *					the worker itself as soon as it's up and running) | ||||
|  * av_dsa_handle	handle for allocatable shared memory | ||||
|  * av_workItems		work item array | ||||
|  * | ||||
|  * This struct is protected by AutovacuumLock, except for av_signal and parts | ||||
|  * of the worker list (see above).  av_dsa_handle is readable unlocked. | ||||
|  * of the worker list (see above). | ||||
|  *------------- | ||||
|  */ | ||||
| typedef struct | ||||
| @ -268,8 +286,7 @@ typedef struct | ||||
| 	dlist_head	av_freeWorkers; | ||||
| 	dlist_head	av_runningWorkers; | ||||
| 	WorkerInfo	av_startingWorker; | ||||
| 	dsa_handle	av_dsa_handle; | ||||
| 	dsa_pointer av_workitems; | ||||
| 	AutoVacuumWorkItem av_workItems[NUM_WORKITEMS]; | ||||
| } AutoVacuumShmemStruct; | ||||
| 
 | ||||
| static AutoVacuumShmemStruct *AutoVacuumShmem; | ||||
| @ -284,32 +301,6 @@ static MemoryContext DatabaseListCxt = NULL; | ||||
| /* Pointer to my own WorkerInfo, valid on each worker */ | ||||
| static WorkerInfo MyWorkerInfo = NULL; | ||||
| 
 | ||||
| /*
 | ||||
|  * Autovacuum workitem array, stored in AutoVacuumShmem->av_workitems.  This | ||||
|  * list is mostly protected by AutovacuumLock, except that if it's marked | ||||
|  * 'active' other processes must not modify the work-identifying members, | ||||
|  * though changing the list pointers is okay. | ||||
|  */ | ||||
| typedef struct AutoVacuumWorkItem | ||||
| { | ||||
| 	AutoVacuumWorkItemType avw_type; | ||||
| 	Oid			avw_database; | ||||
| 	Oid			avw_relation; | ||||
| 	BlockNumber avw_blockNumber; | ||||
| 	bool		avw_active; | ||||
| 	dsa_pointer avw_next;		/* doubly linked list pointers */ | ||||
| 	dsa_pointer avw_prev; | ||||
| } AutoVacuumWorkItem; | ||||
| 
 | ||||
| #define NUM_WORKITEMS	256 | ||||
| typedef struct | ||||
| { | ||||
| 	dsa_pointer avs_usedItems; | ||||
| 	dsa_pointer avs_freeItems; | ||||
| } AutovacWorkItems; | ||||
| 
 | ||||
| static dsa_area *AutoVacuumDSA = NULL; | ||||
| 
 | ||||
| /* PID of launcher, valid only in worker while shutting down */ | ||||
| int			AutovacuumLauncherPid = 0; | ||||
| 
 | ||||
| @ -356,8 +347,6 @@ static void av_sighup_handler(SIGNAL_ARGS); | ||||
| static void avl_sigusr2_handler(SIGNAL_ARGS); | ||||
| static void avl_sigterm_handler(SIGNAL_ARGS); | ||||
| static void autovac_refresh_stats(void); | ||||
| static void remove_wi_from_list(dsa_pointer *list, dsa_pointer wi_ptr); | ||||
| static void add_wi_to_list(dsa_pointer *list, dsa_pointer wi_ptr); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -631,29 +620,6 @@ AutoVacLauncherMain(int argc, char *argv[]) | ||||
| 	 */ | ||||
| 	rebuild_database_list(InvalidOid); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set up our DSA so that backends can install work-item requests.  It may | ||||
| 	 * already exist as created by a previous launcher; and we may even be | ||||
| 	 * already attached to it, if we're here after longjmp'ing above. | ||||
| 	 */ | ||||
| 	if (!AutoVacuumShmem->av_dsa_handle) | ||||
| 	{ | ||||
| 		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); | ||||
| 		AutoVacuumDSA = dsa_create(AutovacuumLock->tranche); | ||||
| 		/* make sure it doesn't go away even if we do */ | ||||
| 		dsa_pin(AutoVacuumDSA); | ||||
| 		dsa_pin_mapping(AutoVacuumDSA); | ||||
| 		AutoVacuumShmem->av_dsa_handle = dsa_get_handle(AutoVacuumDSA); | ||||
| 		/* delay array allocation until first request */ | ||||
| 		AutoVacuumShmem->av_workitems = InvalidDsaPointer; | ||||
| 		LWLockRelease(AutovacuumLock); | ||||
| 	} | ||||
| 	else if (AutoVacuumDSA == NULL) | ||||
| 	{ | ||||
| 		AutoVacuumDSA = dsa_attach(AutoVacuumShmem->av_dsa_handle); | ||||
| 		dsa_pin_mapping(AutoVacuumDSA); | ||||
| 	} | ||||
| 
 | ||||
| 	/* loop until shutdown request */ | ||||
| 	while (!got_SIGTERM) | ||||
| 	{ | ||||
| @ -1697,14 +1663,6 @@ AutoVacWorkerMain(int argc, char *argv[]) | ||||
| 	{ | ||||
| 		char		dbname[NAMEDATALEN]; | ||||
| 
 | ||||
| 		if (AutoVacuumShmem->av_dsa_handle) | ||||
| 		{ | ||||
| 			/* First use of DSA in this worker, so attach to it */ | ||||
| 			Assert(!AutoVacuumDSA); | ||||
| 			AutoVacuumDSA = dsa_attach(AutoVacuumShmem->av_dsa_handle); | ||||
| 			dsa_pin_mapping(AutoVacuumDSA); | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Report autovac startup to the stats collector.  We deliberately do | ||||
| 		 * this before InitPostgres, so that the last_autovac_time will get | ||||
| @ -1987,6 +1945,7 @@ do_autovacuum(void) | ||||
| 	int			effective_multixact_freeze_max_age; | ||||
| 	bool		did_vacuum = false; | ||||
| 	bool		found_concurrent_worker = false; | ||||
| 	int			i; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * StartTransactionCommand and CommitTransactionCommand will automatically | ||||
| @ -2557,65 +2516,40 @@ deleted: | ||||
| 	/*
 | ||||
| 	 * Perform additional work items, as requested by backends. | ||||
| 	 */ | ||||
| 	if (AutoVacuumShmem->av_workitems) | ||||
| 	LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); | ||||
| 	for (i = 0; i < NUM_WORKITEMS; i++) | ||||
| 	{ | ||||
| 		dsa_pointer wi_ptr; | ||||
| 		AutovacWorkItems *workitems; | ||||
| 		AutoVacuumWorkItem *workitem = &AutoVacuumShmem->av_workItems[i]; | ||||
| 
 | ||||
| 		if (!workitem->avw_used) | ||||
| 			continue; | ||||
| 		if (workitem->avw_active) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* claim this one, and release lock while performing it */ | ||||
| 		workitem->avw_active = true; | ||||
| 		LWLockRelease(AutovacuumLock); | ||||
| 
 | ||||
| 		perform_work_item(workitem); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Check for config changes before acquiring lock for further | ||||
| 		 * jobs. | ||||
| 		 */ | ||||
| 		CHECK_FOR_INTERRUPTS(); | ||||
| 		if (got_SIGHUP) | ||||
| 		{ | ||||
| 			got_SIGHUP = false; | ||||
| 			ProcessConfigFile(PGC_SIGHUP); | ||||
| 		} | ||||
| 
 | ||||
| 		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Scan the list of pending items, and process the inactive ones in | ||||
| 		 * our database. | ||||
| 		 */ | ||||
| 		workitems = (AutovacWorkItems *) | ||||
| 			dsa_get_address(AutoVacuumDSA, AutoVacuumShmem->av_workitems); | ||||
| 		wi_ptr = workitems->avs_usedItems; | ||||
| 
 | ||||
| 		while (wi_ptr != InvalidDsaPointer) | ||||
| 		{ | ||||
| 			AutoVacuumWorkItem *workitem; | ||||
| 
 | ||||
| 			workitem = (AutoVacuumWorkItem *) | ||||
| 				dsa_get_address(AutoVacuumDSA, wi_ptr); | ||||
| 
 | ||||
| 			if (workitem->avw_database == MyDatabaseId && !workitem->avw_active) | ||||
| 			{ | ||||
| 				dsa_pointer next_ptr; | ||||
| 
 | ||||
| 				/* claim this one */ | ||||
| 				workitem->avw_active = true; | ||||
| 
 | ||||
| 				LWLockRelease(AutovacuumLock); | ||||
| 
 | ||||
| 				perform_work_item(workitem); | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * Check for config changes before acquiring lock for further | ||||
| 				 * jobs. | ||||
| 				 */ | ||||
| 				CHECK_FOR_INTERRUPTS(); | ||||
| 				if (got_SIGHUP) | ||||
| 				{ | ||||
| 					got_SIGHUP = false; | ||||
| 					ProcessConfigFile(PGC_SIGHUP); | ||||
| 				} | ||||
| 
 | ||||
| 				LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); | ||||
| 
 | ||||
| 				/* Put the array item back for the next user */ | ||||
| 				next_ptr = workitem->avw_next; | ||||
| 				remove_wi_from_list(&workitems->avs_usedItems, wi_ptr); | ||||
| 				add_wi_to_list(&workitems->avs_freeItems, wi_ptr); | ||||
| 				wi_ptr = next_ptr; | ||||
| 			} | ||||
| 			else | ||||
| 				wi_ptr = workitem->avw_next; | ||||
| 		} | ||||
| 
 | ||||
| 		/* all done */ | ||||
| 		LWLockRelease(AutovacuumLock); | ||||
| 		/* and mark it done */ | ||||
| 		workitem->avw_active = false; | ||||
| 		workitem->avw_used = false; | ||||
| 	} | ||||
| 	LWLockRelease(AutovacuumLock); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We leak table_toast_map here (among other things), but since we're | ||||
| @ -3252,104 +3186,32 @@ void | ||||
| AutoVacuumRequestWork(AutoVacuumWorkItemType type, Oid relationId, | ||||
| 					  BlockNumber blkno) | ||||
| { | ||||
| 	AutovacWorkItems *workitems; | ||||
| 	dsa_pointer wi_ptr; | ||||
| 	AutoVacuumWorkItem *workitem; | ||||
| 	int			i; | ||||
| 
 | ||||
| 	LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * It may be useful to de-duplicate the list upon insertion.  For the only | ||||
| 	 * currently existing caller, this is not necessary. | ||||
| 	 * Locate an unused work item and fill it with the given data. | ||||
| 	 */ | ||||
| 
 | ||||
| 	/* First use in this process?  Set up DSA */ | ||||
| 	if (!AutoVacuumDSA) | ||||
| 	for (i = 0; i < NUM_WORKITEMS; i++) | ||||
| 	{ | ||||
| 		if (!AutoVacuumShmem->av_dsa_handle) | ||||
| 		{ | ||||
| 			/* autovacuum launcher not started; nothing can be done */ | ||||
| 			LWLockRelease(AutovacuumLock); | ||||
| 			return; | ||||
| 		} | ||||
| 		AutoVacuumDSA = dsa_attach(AutoVacuumShmem->av_dsa_handle); | ||||
| 		dsa_pin_mapping(AutoVacuumDSA); | ||||
| 		AutoVacuumWorkItem *workitem = &AutoVacuumShmem->av_workItems[i]; | ||||
| 
 | ||||
| 		if (workitem->avw_used) | ||||
| 			continue; | ||||
| 
 | ||||
| 		workitem->avw_used = true; | ||||
| 		workitem->avw_active = false; | ||||
| 		workitem->avw_type = type; | ||||
| 		workitem->avw_database = MyDatabaseId; | ||||
| 		workitem->avw_relation = relationId; | ||||
| 		workitem->avw_blockNumber = blkno; | ||||
| 
 | ||||
| 		/* done */ | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	/* First use overall?  Allocate work items array */ | ||||
| 	if (AutoVacuumShmem->av_workitems == InvalidDsaPointer) | ||||
| 	{ | ||||
| 		int			i; | ||||
| 		AutovacWorkItems *workitems; | ||||
| 
 | ||||
| 		AutoVacuumShmem->av_workitems = | ||||
| 			dsa_allocate_extended(AutoVacuumDSA, | ||||
| 								  sizeof(AutovacWorkItems) + | ||||
| 								  NUM_WORKITEMS * sizeof(AutoVacuumWorkItem), | ||||
| 								  DSA_ALLOC_NO_OOM); | ||||
| 		/* if out of memory, silently disregard the request */ | ||||
| 		if (AutoVacuumShmem->av_workitems == InvalidDsaPointer) | ||||
| 		{ | ||||
| 			LWLockRelease(AutovacuumLock); | ||||
| 			dsa_detach(AutoVacuumDSA); | ||||
| 			AutoVacuumDSA = NULL; | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Initialize each array entry as a member of the free list */ | ||||
| 		workitems = dsa_get_address(AutoVacuumDSA, AutoVacuumShmem->av_workitems); | ||||
| 
 | ||||
| 		workitems->avs_usedItems = InvalidDsaPointer; | ||||
| 		workitems->avs_freeItems = InvalidDsaPointer; | ||||
| 		for (i = 0; i < NUM_WORKITEMS; i++) | ||||
| 		{ | ||||
| 			/* XXX surely there is a simpler way to do this */ | ||||
| 			wi_ptr = AutoVacuumShmem->av_workitems + sizeof(AutovacWorkItems) + | ||||
| 				sizeof(AutoVacuumWorkItem) * i; | ||||
| 			workitem = (AutoVacuumWorkItem *) dsa_get_address(AutoVacuumDSA, wi_ptr); | ||||
| 
 | ||||
| 			workitem->avw_type = 0; | ||||
| 			workitem->avw_database = InvalidOid; | ||||
| 			workitem->avw_relation = InvalidOid; | ||||
| 			workitem->avw_active = false; | ||||
| 
 | ||||
| 			/* put this item in the free list */ | ||||
| 			workitem->avw_next = workitems->avs_freeItems; | ||||
| 			workitems->avs_freeItems = wi_ptr; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	workitems = (AutovacWorkItems *) | ||||
| 		dsa_get_address(AutoVacuumDSA, AutoVacuumShmem->av_workitems); | ||||
| 
 | ||||
| 	/* If array is full, disregard the request */ | ||||
| 	if (workitems->avs_freeItems == InvalidDsaPointer) | ||||
| 	{ | ||||
| 		LWLockRelease(AutovacuumLock); | ||||
| 		dsa_detach(AutoVacuumDSA); | ||||
| 		AutoVacuumDSA = NULL; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* remove workitem struct from free list ... */ | ||||
| 	wi_ptr = workitems->avs_freeItems; | ||||
| 	remove_wi_from_list(&workitems->avs_freeItems, wi_ptr); | ||||
| 
 | ||||
| 	/* ... initialize it ... */ | ||||
| 	workitem = dsa_get_address(AutoVacuumDSA, wi_ptr); | ||||
| 	workitem->avw_type = type; | ||||
| 	workitem->avw_database = MyDatabaseId; | ||||
| 	workitem->avw_relation = relationId; | ||||
| 	workitem->avw_blockNumber = blkno; | ||||
| 	workitem->avw_active = false; | ||||
| 
 | ||||
| 	/* ... and put it on autovacuum's to-do list */ | ||||
| 	add_wi_to_list(&workitems->avs_usedItems, wi_ptr); | ||||
| 
 | ||||
| 	LWLockRelease(AutovacuumLock); | ||||
| 
 | ||||
| 	dsa_detach(AutoVacuumDSA); | ||||
| 	AutoVacuumDSA = NULL; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -3429,6 +3291,8 @@ AutoVacuumShmemInit(void) | ||||
| 		dlist_init(&AutoVacuumShmem->av_freeWorkers); | ||||
| 		dlist_init(&AutoVacuumShmem->av_runningWorkers); | ||||
| 		AutoVacuumShmem->av_startingWorker = NULL; | ||||
| 		memset(AutoVacuumShmem->av_workItems, 0, | ||||
| 			   sizeof(AutoVacuumWorkItem) * NUM_WORKITEMS); | ||||
| 
 | ||||
| 		worker = (WorkerInfo) ((char *) AutoVacuumShmem + | ||||
| 							   MAXALIGN(sizeof(AutoVacuumShmemStruct))); | ||||
| @ -3473,59 +3337,3 @@ autovac_refresh_stats(void) | ||||
| 
 | ||||
| 	pgstat_clear_snapshot(); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Simplistic open-coded list implementation for objects stored in DSA. | ||||
|  * Each item is doubly linked, but we have no tail pointer, and the "prev" | ||||
|  * element of the first item is null, not the list. | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Remove a work item from the given list. | ||||
|  */ | ||||
| static void | ||||
| remove_wi_from_list(dsa_pointer *list, dsa_pointer wi_ptr) | ||||
| { | ||||
| 	AutoVacuumWorkItem *workitem = dsa_get_address(AutoVacuumDSA, wi_ptr); | ||||
| 	dsa_pointer next = workitem->avw_next; | ||||
| 	dsa_pointer prev = workitem->avw_prev; | ||||
| 
 | ||||
| 	workitem->avw_next = workitem->avw_prev = InvalidDsaPointer; | ||||
| 
 | ||||
| 	if (next != InvalidDsaPointer) | ||||
| 	{ | ||||
| 		workitem = dsa_get_address(AutoVacuumDSA, next); | ||||
| 		workitem->avw_prev = prev; | ||||
| 	} | ||||
| 
 | ||||
| 	if (prev != InvalidDsaPointer) | ||||
| 	{ | ||||
| 		workitem = dsa_get_address(AutoVacuumDSA, prev); | ||||
| 		workitem->avw_next = next; | ||||
| 	} | ||||
| 	else | ||||
| 		*list = next; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Add a workitem to the given list | ||||
|  */ | ||||
| static void | ||||
| add_wi_to_list(dsa_pointer *list, dsa_pointer wi_ptr) | ||||
| { | ||||
| 	if (*list == InvalidDsaPointer) | ||||
| 	{ | ||||
| 		/* list is empty; item is now singleton */ | ||||
| 		*list = wi_ptr; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		AutoVacuumWorkItem *workitem = dsa_get_address(AutoVacuumDSA, wi_ptr); | ||||
| 		AutoVacuumWorkItem *old = dsa_get_address(AutoVacuumDSA, *list); | ||||
| 
 | ||||
| 		/* Put item at head of list */ | ||||
| 		workitem->avw_next = *list; | ||||
| 		old->avw_prev = wi_ptr; | ||||
| 		*list = wi_ptr; | ||||
| 	} | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user