 src/backend/utils/resowner/resowner.c | 76 +++++++++++++++++++++++------------
 1 file changed, 50 insertions(+), 26 deletions(-)

diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 0955bcc..ef6e6bd 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -229,6 +229,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	ResourceOwner child;
 	ResourceOwner save;
 	ResourceReleaseCallbackItem *item;
+	int		i;
 
 	/* Recurse to handle descendants */
 	for (child = owner->firstchild; child != NULL; child = child->nextchild)
@@ -256,11 +257,13 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * We are careful to do the releasing back-to-front, so as to avoid
 		 * O(N^2) behavior in ResourceOwnerForgetBuffer().
 		 */
-		while (owner->nbuffers > 0)
+		for (i=0; owner->nbuffers > 0; i = (i + 1) % owner->maxbuffers)
 		{
+			if (!BufferIsValid(owner->buffers[i]))
+				continue;
 			if (isCommit)
-				PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
-			ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
+				PrintBufferLeakWarning(owner->buffers[i]);
+			ReleaseBuffer(owner->buffers[i]);
 		}
 
 		/*
@@ -578,21 +581,38 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 	int			newmax;
 
 	if (owner == NULL ||
-		owner->nbuffers < owner->maxbuffers)
+		owner->nbuffers < owner->maxbuffers / 2)
 		return;					/* nothing to do */
 
 	if (owner->buffers == NULL)
 	{
 		newmax = 16;
 		owner->buffers = (Buffer *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
+			MemoryContextAllocZero(TopMemoryContext, newmax * sizeof(Buffer));
 		owner->maxbuffers = newmax;
 	}
 	else
 	{
+		Buffer *newbuf;
+		int		i, j;
+
 		newmax = owner->maxbuffers * 2;
-		owner->buffers = (Buffer *)
-			repalloc(owner->buffers, newmax * sizeof(Buffer));
+		newbuf = (Buffer *)
+			MemoryContextAllocZero(TopMemoryContext, newmax * sizeof(Buffer));
+
+		for (i=0; i < owner->maxbuffers; i++)
+		{
+			Buffer	buffer = owner->buffers[i];
+
+			if (!BufferIsValid(buffer))
+				continue;
+			j = hash_uint32((uint32)buffer) % newmax;
+			while (BufferIsValid(newbuf[j]))
+				j = (j + 1) % newmax;
+			newbuf[j] = buffer;
+		}
+		pfree(owner->buffers);
+		owner->buffers = newbuf;
 		owner->maxbuffers = newmax;
 	}
 }
@@ -610,9 +630,21 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 {
 	if (owner != NULL)
 	{
+		int		start = hash_uint32((uint32)buffer) % owner->maxbuffers;
+		int		curr = start;
+
 		Assert(owner->nbuffers < owner->maxbuffers);
-		owner->buffers[owner->nbuffers] = buffer;
-		owner->nbuffers++;
+		do {
+			if (!BufferIsValid(owner->buffers[curr]))
+			{
+				owner->buffers[curr] = buffer;
+				owner->nbuffers++;
+				return;
+			}
+			curr = (curr + 1) % owner->maxbuffers;
+		} while (curr != start);
+		Assert(false);
+		elog(ERROR, "Couldn't find a slot to remember buffer");
 	}
 }
 
@@ -628,27 +660,19 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 	if (owner != NULL)
 	{
 		Buffer	   *buffers = owner->buffers;
-		int			nb1 = owner->nbuffers - 1;
-		int			i;
+		int			start = hash_uint32((uint32)buffer) % owner->maxbuffers;
+		int			curr = start;
 
-		/*
-		 * Scan back-to-front because it's more likely we are releasing a
-		 * recently pinned buffer.  This isn't always the case of course, but
-		 * it's the way to bet.
-		 */
-		for (i = nb1; i >= 0; i--)
-		{
-			if (buffers[i] == buffer)
+		do {
+			if (buffers[curr] == buffer)
 			{
-				while (i < nb1)
-				{
-					buffers[i] = buffers[i + 1];
-					i++;
-				}
-				owner->nbuffers = nb1;
+				buffers[curr] = InvalidBuffer;
+				owner->nbuffers--;
 				return;
 			}
-		}
+			curr = (curr + 1) % owner->maxbuffers;
+		} while (curr != start);
+		Assert(false);
 		elog(ERROR, "buffer %d is not owned by resource owner %s",
 			 buffer, owner->name);
 	}
