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