PG-244: Fix race condition in get_next_wbucket().

The if condition bellow in geta_next_wbucket() was subject to a race
condition:
if ((current_usec - pgss->prev_bucket_usec) > (PGSM_BUCKET_TIME * 1000 *
1000))

Two or more threads/processes could easily evaluate this condition to
true, thus executing more than once the block that would calculate a
new bucket id, clear/move old entries in the pgss_query_hash and
pgss_hash hash tables.

To avoid this problem, we define prev_bucket_usec and current_wbucket
variables as atomic and execute a loop to check if another thread has
updated prev_bucket_usec before the current one.
This commit is contained in:
Diego Fronza
2021-09-30 17:13:27 -03:00
parent f269af3da2
commit 89743e9243
3 changed files with 70 additions and 50 deletions

View File

@@ -146,7 +146,7 @@ hash_entry_alloc(pgssSharedState *pgss, pgssHashKey *key, int encoding)
entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER_NULL, &found);
if (!found)
{
pgss->bucket_entry[pgss->current_wbucket]++;
pgss->bucket_entry[pg_atomic_read_u64(&pgss->current_wbucket)]++;
/* New entry, initialize it */
/* reset the statistics */
memset(&entry->counters, 0, sizeof(Counters));
@@ -236,22 +236,6 @@ hash_entry_dealloc(int new_bucket_id, int old_bucket_id)
pgssEntry *entry = NULL;
List *pending_entries = NIL;
ListCell *pending_entry;
/*
* During transition to a new bucket id, a rare but possible race
* condition may happen while reading pgss->current_wbucket. If a
* different thread/process updates pgss->current_wbucket before this
* function is called, it may happen that old_bucket_id == new_bucket_id.
* If that is the case, we adjust the old bucket id here instead of using
* a lock in order to avoid the overhead.
*/
if (old_bucket_id != -1 && old_bucket_id == new_bucket_id)
{
if (old_bucket_id == 0)
old_bucket_id = PGSM_MAX_BUCKETS - 1;
else
old_bucket_id--;
}
hash_seq_init(&hash_seq, pgss_hash);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
@@ -344,7 +328,7 @@ hash_entry_reset()
{
hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
}
pgss->current_wbucket = 0;
pg_atomic_write_u64(&pgss->current_wbucket, 0);
LWLockRelease(pgss->lock);
}