Add revalidation for clean pages under lock to ensure queue integrity (#21793)
* Prevent potential race conditions by revalidating page status after acquiring the clean lock. * Handle inconsistent states by logging and updating flags appropriately.
Stelios Fragkakis committed
Feb 21, 2026 at 17:55 UTC
2733e6fc60cf9c76a840e70c554dff5a6b6e01e2
1 file changed
+13
-3
src/database/engine/cache.c
+13
-3
@@ -765,10 +765,20 @@ static ALWAYS_INLINE void page_has_been_accessed(PGC *cache, PGC_PAGE *page) {
765
766
if (flags & PGC_PAGE_CLEAN) {
767
if(pgc_queue_trylock(cache, &cache->clean, PGC_QUEUE_LOCK_PRIO_EVICTORS)) {
768
- DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(cache->clean.base, page, link.prev, link.next);
769
- DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(cache->clean.base, page, link.prev, link.next);
768
+ // The status check above is lockless. Re-validate under the clean lock to avoid
769
+ // touching clean-list pointers after the page moved to another queue.
770
+ if(is_page_clean(page)) {
771
+ DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(cache->clean.base, page, link.prev, link.next);
772
+ DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(cache->clean.base, page, link.prev, link.next);
773
+ page_flag_clear(page, PGC_PAGE_HAS_BEEN_ACCESSED);
774
+ }
775
+ else {
776
+ // Expected concurrent transition: page may move clean -> dirty/hot
777
+ // between lockless flag read and acquiring the clean queue lock.
778
+ page_flag_set(page, PGC_PAGE_HAS_BEEN_ACCESSED);
779
+ }
780
+
781
pgc_queue_unlock(cache, &cache->clean);
771
- page_flag_clear(page, PGC_PAGE_HAS_BEEN_ACCESSED);
782
}
783
else
784
page_flag_set(page, PGC_PAGE_HAS_BEEN_ACCESSED);