@@ -4402,8 +4402,8 @@ write_relcache_init_file(bool shared)
4402
4402
* updated by SI message processing, but we can't be sure whether what we
4403
4403
* wrote out was up-to-date.)
4404
4404
*
4405
- * This mustn't run concurrently with RelationCacheInitFileInvalidate, so
4406
- * grab a serialization lock for the duration.
4405
+ * This mustn't run concurrently with the code that unlinks an init file
4406
+ * and sends SI messages, so grab a serialization lock for the duration.
4407
4407
*/
4408
4408
LWLockAcquire (RelCacheInitLock , LW_EXCLUSIVE );
4409
4409
@@ -4467,19 +4467,22 @@ RelationIdIsInInitFile(Oid relationId)
4467
4467
* changed one or more of the relation cache entries that are kept in the
4468
4468
* local init file.
4469
4469
*
4470
- * We actually need to remove the init file twice: once just before sending
4471
- * the SI messages that include relcache inval for such relations, and once
4472
- * just after sending them. The unlink before ensures that a backend that's
4473
- * currently starting cannot read the now-obsolete init file and then miss
4474
- * the SI messages that will force it to update its relcache entries. (This
4475
- * works because the backend startup sequence gets into the PGPROC array before
4476
- * trying to load the init file.) The unlink after is to synchronize with a
4477
- * backend that may currently be trying to write an init file based on data
4478
- * that we've just rendered invalid. Such a backend will see the SI messages,
4479
- * but we can't leave the init file sitting around to fool later backends.
4470
+ * To be safe against concurrent inspection or rewriting of the init file,
4471
+ * we must take RelCacheInitLock, then remove the old init file, then send
4472
+ * the SI messages that include relcache inval for such relations, and then
4473
+ * release RelCacheInitLock. This serializes the whole affair against
4474
+ * write_relcache_init_file, so that we can be sure that any other process
4475
+ * that's concurrently trying to create a new init file won't move an
4476
+ * already-stale version into place after we unlink. Also, because we unlink
4477
+ * before sending the SI messages, a backend that's currently starting cannot
4478
+ * read the now-obsolete init file and then miss the SI messages that will
4479
+ * force it to update its relcache entries. (This works because the backend
4480
+ * startup sequence gets into the sinval array before trying to load the init
4481
+ * file.)
4480
4482
*
4481
- * Ignore any failure to unlink the file, since it might not be there if
4482
- * no backend has been started since the last removal.
4483
+ * We take the lock and do the unlink in RelationCacheInitFilePreInvalidate,
4484
+ * then release the lock in RelationCacheInitFilePostInvalidate. Caller must
4485
+ * send any pending SI messages between those calls.
4483
4486
*
4484
4487
* Notice this deals only with the local init file, not the shared init file.
4485
4488
* The reason is that there can never be a "significant" change to the
@@ -4489,34 +4492,37 @@ RelationIdIsInInitFile(Oid relationId)
4489
4492
* be invalid enough to make it necessary to remove it.
4490
4493
*/
4491
4494
void
4492
- RelationCacheInitFileInvalidate ( bool beforeSend )
4495
+ RelationCacheInitFilePreInvalidate ( void )
4493
4496
{
4494
4497
char initfilename [MAXPGPATH ];
4495
4498
4496
4499
snprintf (initfilename , sizeof (initfilename ), "%s/%s" ,
4497
4500
DatabasePath , RELCACHE_INIT_FILENAME );
4498
4501
4499
- if (beforeSend )
4500
- {
4501
- /* no interlock needed here */
4502
- unlink (initfilename );
4503
- }
4504
- else
4502
+ LWLockAcquire (RelCacheInitLock , LW_EXCLUSIVE );
4503
+
4504
+ if (unlink (initfilename ) < 0 )
4505
4505
{
4506
4506
/*
4507
- * We need to interlock this against write_relcache_init_file, to
4508
- * guard against possibility that someone renames a new-but-
4509
- * already-obsolete init file into place just after we unlink. With
4510
- * the interlock, it's certain that write_relcache_init_file will
4511
- * notice our SI inval message before renaming into place, or else
4512
- * that we will execute second and successfully unlink the file.
4507
+ * The file might not be there if no backend has been started since
4508
+ * the last removal. But complain about failures other than ENOENT.
4509
+ * Fortunately, it's not too late to abort the transaction if we
4510
+ * can't get rid of the would-be-obsolete init file.
4513
4511
*/
4514
- LWLockAcquire (RelCacheInitLock , LW_EXCLUSIVE );
4515
- unlink (initfilename );
4516
- LWLockRelease (RelCacheInitLock );
4512
+ if (errno != ENOENT )
4513
+ ereport (ERROR ,
4514
+ (errcode_for_file_access (),
4515
+ errmsg ("could not remove cache file \"%s\": %m" ,
4516
+ initfilename )));
4517
4517
}
4518
4518
}
4519
4519
4520
+ void
4521
+ RelationCacheInitFilePostInvalidate (void )
4522
+ {
4523
+ LWLockRelease (RelCacheInitLock );
4524
+ }
4525
+
4520
4526
/*
4521
4527
* Remove the init files during postmaster startup.
4522
4528
*
0 commit comments