Teach SLRU code to avoid replacing I/O-busy pages.
Patch by me; review by Tom Lane and others.
This commit is contained in:
parent
d5375491f8
commit
3ae5133b1c
@ -403,12 +403,6 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
|
|||||||
/* Acquire per-buffer lock (cannot deadlock, see notes at top) */
|
/* Acquire per-buffer lock (cannot deadlock, see notes at top) */
|
||||||
LWLockAcquire(shared->buffer_locks[slotno], LW_EXCLUSIVE);
|
LWLockAcquire(shared->buffer_locks[slotno], LW_EXCLUSIVE);
|
||||||
|
|
||||||
/*
|
|
||||||
* Temporarily mark page as recently-used to discourage
|
|
||||||
* SlruSelectLRUPage from selecting it again for someone else.
|
|
||||||
*/
|
|
||||||
SlruRecentlyUsed(shared, slotno);
|
|
||||||
|
|
||||||
/* Release control lock while doing I/O */
|
/* Release control lock while doing I/O */
|
||||||
LWLockRelease(shared->ControlLock);
|
LWLockRelease(shared->ControlLock);
|
||||||
|
|
||||||
@ -909,9 +903,12 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
|
|||||||
{
|
{
|
||||||
int slotno;
|
int slotno;
|
||||||
int cur_count;
|
int cur_count;
|
||||||
int bestslot;
|
int bestvalidslot = 0; /* keep compiler quiet */
|
||||||
int best_delta;
|
int best_valid_delta = -1;
|
||||||
int best_page_number;
|
int best_valid_page_number = 0; /* keep compiler quiet */
|
||||||
|
int bestinvalidslot = 0; /* keep compiler quiet */
|
||||||
|
int best_invalid_delta = -1;
|
||||||
|
int best_invalid_page_number = 0; /* keep compiler quiet */
|
||||||
|
|
||||||
/* See if page already has a buffer assigned */
|
/* See if page already has a buffer assigned */
|
||||||
for (slotno = 0; slotno < shared->num_slots; slotno++)
|
for (slotno = 0; slotno < shared->num_slots; slotno++)
|
||||||
@ -922,8 +919,16 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we find any EMPTY slot, just select that one. Else locate the
|
* If we find any EMPTY slot, just select that one. Else choose a
|
||||||
* least-recently-used slot to replace.
|
* victim page to replace. We normally take the least recently used
|
||||||
|
* valid page, but we will never take the slot containing
|
||||||
|
* latest_page_number, even if it appears least recently used. We
|
||||||
|
* will select a slot that is already I/O busy only if there is no
|
||||||
|
* other choice: a read-busy slot will not be least recently used once
|
||||||
|
* the read finishes, and waiting for an I/O on a write-busy slot is
|
||||||
|
* inferior to just picking some other slot. Testing shows the slot
|
||||||
|
* we pick instead will often be clean, allowing us to begin a read
|
||||||
|
* at once.
|
||||||
*
|
*
|
||||||
* Normally the page_lru_count values will all be different and so
|
* Normally the page_lru_count values will all be different and so
|
||||||
* there will be a well-defined LRU page. But since we allow
|
* there will be a well-defined LRU page. But since we allow
|
||||||
@ -932,9 +937,6 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
|
|||||||
* acquire the same lru_count values. In that case we break ties by
|
* acquire the same lru_count values. In that case we break ties by
|
||||||
* choosing the furthest-back page.
|
* choosing the furthest-back page.
|
||||||
*
|
*
|
||||||
* In no case will we select the slot containing latest_page_number
|
|
||||||
* for replacement, even if it appears least recently used.
|
|
||||||
*
|
|
||||||
* Notice that this next line forcibly advances cur_lru_count to a
|
* Notice that this next line forcibly advances cur_lru_count to a
|
||||||
* value that is certainly beyond any value that will be in the
|
* value that is certainly beyond any value that will be in the
|
||||||
* page_lru_count array after the loop finishes. This ensures that
|
* page_lru_count array after the loop finishes. This ensures that
|
||||||
@ -944,9 +946,6 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
|
|||||||
* multiple pages with the same lru_count.
|
* multiple pages with the same lru_count.
|
||||||
*/
|
*/
|
||||||
cur_count = (shared->cur_lru_count)++;
|
cur_count = (shared->cur_lru_count)++;
|
||||||
best_delta = -1;
|
|
||||||
bestslot = 0; /* no-op, just keeps compiler quiet */
|
|
||||||
best_page_number = 0; /* ditto */
|
|
||||||
for (slotno = 0; slotno < shared->num_slots; slotno++)
|
for (slotno = 0; slotno < shared->num_slots; slotno++)
|
||||||
{
|
{
|
||||||
int this_delta;
|
int this_delta;
|
||||||
@ -968,34 +967,57 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
|
|||||||
this_delta = 0;
|
this_delta = 0;
|
||||||
}
|
}
|
||||||
this_page_number = shared->page_number[slotno];
|
this_page_number = shared->page_number[slotno];
|
||||||
if ((this_delta > best_delta ||
|
if (this_page_number == shared->latest_page_number)
|
||||||
(this_delta == best_delta &&
|
continue;
|
||||||
ctl->PagePrecedes(this_page_number, best_page_number))) &&
|
if (shared->page_status[slotno] == SLRU_PAGE_VALID)
|
||||||
this_page_number != shared->latest_page_number)
|
|
||||||
{
|
{
|
||||||
bestslot = slotno;
|
if (this_delta > best_valid_delta ||
|
||||||
best_delta = this_delta;
|
(this_delta == best_valid_delta &&
|
||||||
best_page_number = this_page_number;
|
ctl->PagePrecedes(this_page_number,
|
||||||
|
best_valid_page_number)))
|
||||||
|
{
|
||||||
|
bestvalidslot = slotno;
|
||||||
|
best_valid_delta = this_delta;
|
||||||
|
best_valid_page_number = this_page_number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (this_delta > best_invalid_delta ||
|
||||||
|
(this_delta == best_invalid_delta &&
|
||||||
|
ctl->PagePrecedes(this_page_number,
|
||||||
|
best_invalid_page_number)))
|
||||||
|
{
|
||||||
|
bestinvalidslot = slotno;
|
||||||
|
best_invalid_delta = this_delta;
|
||||||
|
best_invalid_page_number = this_page_number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If all pages (except possibly the latest one) are I/O busy, we'll
|
||||||
|
* have to wait for an I/O to complete and then retry. In that unhappy
|
||||||
|
* case, we choose to wait for the I/O on the least recently used slot,
|
||||||
|
* on the assumption that it was likely initiated first of all the I/Os
|
||||||
|
* in progress and may therefore finish first.
|
||||||
|
*/
|
||||||
|
if (best_valid_delta < 0)
|
||||||
|
{
|
||||||
|
SimpleLruWaitIO(ctl, bestinvalidslot);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the selected page is clean, we're set.
|
* If the selected page is clean, we're set.
|
||||||
*/
|
*/
|
||||||
if (shared->page_status[bestslot] == SLRU_PAGE_VALID &&
|
if (!shared->page_dirty[bestvalidslot])
|
||||||
!shared->page_dirty[bestslot])
|
return bestvalidslot;
|
||||||
return bestslot;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to wait for I/O. Normal case is that it's dirty and we
|
* Write the page.
|
||||||
* must initiate a write, but it's possible that the page is already
|
|
||||||
* write-busy, or in the worst case still read-busy. In those cases
|
|
||||||
* we wait for the existing I/O to complete.
|
|
||||||
*/
|
*/
|
||||||
if (shared->page_status[bestslot] == SLRU_PAGE_VALID)
|
SlruInternalWritePage(ctl, bestvalidslot, NULL);
|
||||||
SlruInternalWritePage(ctl, bestslot, NULL);
|
|
||||||
else
|
|
||||||
SimpleLruWaitIO(ctl, bestslot);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now loop back and try again. This is the easiest way of dealing
|
* Now loop back and try again. This is the easiest way of dealing
|
||||||
|
Loading…
x
Reference in New Issue
Block a user