diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index b423c8fdbe8ae3f9e4f745e31696a0b9f228f99d..87a251915a5e0ae8a747ee24e70530bfea72dc71 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.110 2004/02/03 17:34:02 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.111 2004/02/06 19:36:17 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -586,6 +586,26 @@ btbulkdelete(PG_FUNCTION_ARGS)
 
 			CHECK_FOR_INTERRUPTS();
 
+			/*
+			 * If we're called by a cost based vacuum, do the
+			 * napping in case the balance exceeded the limit.
+			 */
+			if (VacuumCostActive && !InterruptPending &&
+					VacuumCostBalance >= VacuumCostLimit)
+			{
+				int		msec;
+
+				msec = VacuumCostNaptime * VacuumCostBalance / VacuumCostLimit;
+				if (msec < VacuumCostNaptime * 4)
+					PG_MSLEEP(msec);
+				else
+					PG_MSLEEP(VacuumCostNaptime * 4);
+
+				VacuumCostBalance = 0;
+
+				CHECK_FOR_INTERRUPTS();
+			}
+
 			ndeletable = 0;
 			page = BufferGetPage(buf);
 			opaque = (BTPageOpaque) PageGetSpecialPointer(page);
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 14c66b498d302c8e98827293791e52ab4e2bf495..c271152877054a24d2c94033ec0d75fc7b559a8f 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -31,7 +31,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.34 2004/02/03 17:34:02 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.35 2004/02/06 19:36:17 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -148,6 +148,11 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
 	vac_open_indexes(onerel, &nindexes, &Irel);
 	hasindex = (nindexes > 0);
 
+	/* Turn on vacuum cost accounting */
+	if (VacuumCostNaptime > 0)
+		VacuumCostActive = true;
+	VacuumCostBalance = 0;
+
 	/* Do the vacuuming */
 	lazy_scan_heap(onerel, vacrelstats, Irel, nindexes);
 
@@ -168,6 +173,9 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
 	/* Update shared free space map with final free space info */
 	lazy_update_fsm(onerel, vacrelstats);
 
+	/* Turn off vacuum cost accounting */
+	VacuumCostActive = false;
+
 	/* Update statistics in pg_class */
 	vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,
 						vacrelstats->rel_tuples, hasindex);
@@ -228,6 +236,25 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 
 		CHECK_FOR_INTERRUPTS();
 
+		/*
+		 * Do the napping in a cost based vacuum.
+		 */
+		if (VacuumCostActive && !InterruptPending &&
+				VacuumCostBalance >= VacuumCostLimit)
+		{
+			int		msec;
+
+			msec = VacuumCostNaptime * VacuumCostBalance / VacuumCostLimit;
+			if (msec < VacuumCostNaptime * 4)
+				PG_MSLEEP(msec);
+			else
+				PG_MSLEEP(VacuumCostNaptime * 4);
+
+			VacuumCostBalance = 0;
+
+			CHECK_FOR_INTERRUPTS();
+		}
+
 		/*
 		 * If we are close to overrunning the available space for
 		 * dead-tuple TIDs, pause and do a cycle of vacuuming before we
@@ -469,6 +496,25 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats)
 
 		CHECK_FOR_INTERRUPTS();
 
+		/*
+		 * Do the napping in a cost based vacuum.
+		 */
+		if (VacuumCostActive && !InterruptPending &&
+				VacuumCostBalance >= VacuumCostLimit)
+		{
+			int		msec;
+
+			msec = VacuumCostNaptime * VacuumCostBalance / VacuumCostLimit;
+			if (msec < VacuumCostNaptime * 4)
+				PG_MSLEEP(msec);
+			else
+				PG_MSLEEP(VacuumCostNaptime * 4);
+
+			VacuumCostBalance = 0;
+
+			CHECK_FOR_INTERRUPTS();
+		}
+
 		tblk = ItemPointerGetBlockNumber(&vacrelstats->dead_tuples[tupindex]);
 		buf = ReadBuffer(onerel, tblk);
 		LockBufferForCleanup(buf);
@@ -800,6 +846,25 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
 
 		CHECK_FOR_INTERRUPTS();
 
+		/*
+		 * Do the napping in a cost based vacuum.
+		 */
+		if (VacuumCostActive && !InterruptPending &&
+				VacuumCostBalance >= VacuumCostLimit)
+		{
+			int		msec;
+
+			msec = VacuumCostNaptime * VacuumCostBalance / VacuumCostLimit;
+			if (msec < VacuumCostNaptime * 4)
+				PG_MSLEEP(msec);
+			else
+				PG_MSLEEP(VacuumCostNaptime * 4);
+
+			VacuumCostBalance = 0;
+
+			CHECK_FOR_INTERRUPTS();
+		}
+
 		blkno--;
 
 		buf = ReadBuffer(onerel, blkno);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index cb82159aff02f528cf512774677e29bdecef982b..b927b5ea5e7ed164e7b626d965e0095be56248a3 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.155 2004/02/04 01:24:53 wieck Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.156 2004/02/06 19:36:18 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -576,6 +576,12 @@ write_buffer(Buffer buffer, bool release)
 	LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
 	Assert(bufHdr->refcount > 0);
 
+	/*
+	 * If the buffer is not dirty yet, do vacuum cost accounting.
+	 */
+	if (!(bufHdr->flags & BM_DIRTY) && VacuumCostActive)
+		VacuumCostBalance += VacuumCostPageDirty;
+
 	bufHdr->flags |= (BM_DIRTY | BM_JUST_DIRTIED);
 
 	if (release)
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c
index 6388bc724d973f287347c690d6edb4caabc9fd17..74ec4518ab90fdec2b61310df342a862f930987a 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/buffer/freelist.c,v 1.39 2004/01/15 16:14:26 wieck Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/buffer/freelist.c,v 1.40 2004/02/06 19:36:18 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,7 @@
 #include "storage/ipc.h"
 #include "storage/proc.h"
 #include "access/xact.h"
+#include "miscadmin.h"
 
 #ifndef MAX
 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
@@ -237,6 +238,12 @@ StrategyBufferLookup(BufferTag *tagPtr, bool recheck)
 				strategy_get_from = STRAT_LIST_T2;
 		}
 
+		/*
+		 * Do the cost accounting for vacuum
+		 */
+		if (VacuumCostActive)
+			VacuumCostBalance += VacuumCostPageMiss;
+
 		/* report cache miss */
 		return NULL;
 	}
@@ -250,6 +257,8 @@ StrategyBufferLookup(BufferTag *tagPtr, bool recheck)
 	 * Count hits
 	 */
 	StrategyControl->num_hit[cdb->list]++;
+	if (VacuumCostActive)
+		VacuumCostBalance += VacuumCostPageHit;
 
 	/*
 	 * If this is a T2 hit, we simply move the CDB to the
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 1ea95d2e5071cc702aa5fd2a18bd4de620a2ab63..735e5aa69d3e1eb6f2ddb55dc0f13622790baaae 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.388 2004/02/03 17:34:03 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.389 2004/02/06 19:36:18 wieck Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -2707,6 +2707,11 @@ PostgresMain(int argc, char *argv[], const char *username)
 		InError = false;
 		xact_started = false;
 
+		/*
+		 * Clear flag that causes accounting for cost based vacuum.
+		 */
+		VacuumCostActive = false;
+
 		/*
 		 * If we were handling an extended-query-protocol message,
 		 * initiate skip till next Sync.  This also causes us not to issue
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index c170ae603df3baf2c4dfa9ca30e3f8f5190d16a5..f916d013d1c129a0812bf0924395bcef96fd1a88 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/init/globals.c,v 1.82 2004/02/03 17:34:03 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/init/globals.c,v 1.83 2004/02/06 19:36:18 wieck Exp $
  *
  * NOTES
  *	  Globals used all over the place should be declared here and not
@@ -81,3 +81,11 @@ bool		allowSystemTableMods = false;
 int			work_mem = 1024;
 int			maintenance_work_mem = 16384;
 int			NBuffers = 1000;
+
+int			VacuumCostPageHit = 1;
+int			VacuumCostPageMiss = 10;
+int			VacuumCostPageDirty = 20;
+int			VacuumCostLimit = 200;
+int			VacuumCostBalance = 0;
+int			VacuumCostNaptime = 0;
+bool		VacuumCostActive = false;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index eb79ea2c627dab4d1b66779d1e5e5a2bd926ff84..7fe7c33cf8a5be23b68ec3ce5865a728eeb43fde 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.185 2004/02/04 01:24:53 wieck Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.186 2004/02/06 19:36:18 wieck Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -1047,6 +1047,51 @@ static struct config_int ConfigureNamesInt[] =
 		16384, 1024, INT_MAX / 1024, NULL, NULL
 	},
 
+	{
+		{"vacuum_cost_page_hit", PGC_USERSET, RESOURCES,
+			gettext_noop("Vacuum cost for a page found in the buffer cache."),
+			NULL
+		},
+		&VacuumCostPageHit,
+		1, 0, 10000, NULL, NULL
+	},
+
+	{
+		{"vacuum_cost_page_miss", PGC_USERSET, RESOURCES,
+			gettext_noop("Vacuum cost for a page not found in the buffer cache."),
+			NULL
+		},
+		&VacuumCostPageMiss,
+		10, 0, 10000, NULL, NULL
+	},
+
+	{
+		{"vacuum_cost_page_dirty", PGC_USERSET, RESOURCES,
+			gettext_noop("Vacuum cost for a page dirtied by vacuum."),
+			NULL
+		},
+		&VacuumCostPageDirty,
+		20, 0, 10000, NULL, NULL
+	},
+
+	{
+		{"vacuum_cost_limit", PGC_USERSET, RESOURCES,
+			gettext_noop("Vacuum cost amount available before napping."),
+			NULL
+		},
+		&VacuumCostLimit,
+		200, 1, 10000, NULL, NULL
+	},
+
+	{
+		{"vacuum_cost_naptime", PGC_USERSET, RESOURCES,
+			gettext_noop("Vacuum cost naptime in milliseconds."),
+			NULL
+		},
+		&VacuumCostNaptime,
+		0, 0, 1000, NULL, NULL
+	},
+
 	{
 		{"max_files_per_process", PGC_BACKEND, RESOURCES_KERNEL,
 			gettext_noop("Sets the maximum number of simultaneously open files for each server process."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index ad6f6bfcfeff803071aa05db15f688dc3bb3c1dc..ee7b47c04dc1402e06a58bad7fe62c4197ec7d58 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -60,6 +60,12 @@
 #maintenance_work_mem = 16384	# min 1024, size in KB
 #debug_shared_buffers = 0	# 0-600 seconds
 
+#vacuum_cost_page_hit = 1	# 0-10000 credits
+#vacuum_cost_page_miss = 10	# 0-10000 credits
+#vacuum_cost_page_dirty = 20	# 0-10000 credits
+#vacuum_cost_limit = 200		# 0-10000 credits
+#vacuum_cost_naptime = 50	# 0-1000 milliseconds
+
 # - Background writer -
 #bgwriter_delay = 200		# 10-5000 milliseconds
 #bgwriter_percent = 1		# 0-100% of dirty buffers
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index a7fe724533e7f34ab337229540382c7a135e0e88..1f71a434c17a458f3fdc0b3b52f8c248740cf327 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.150 2004/02/03 17:34:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.151 2004/02/06 19:36:18 wieck Exp $
  *
  * NOTES
  *	  some of the information in this file should be moved to
@@ -106,11 +106,19 @@ do { \
 	delay.tv_usec = ((_usec) % 1000000); \
 	(void) select(0, NULL, NULL, NULL, &delay); \
 } while(0)
+#define PG_MSLEEP(_msec) \
+do { \
+	struct timeval _delay; \
+	_delay.tv_sec = (_msec) / 1000; \
+	_delay.tv_usec = ((_msec) % 1000) * 1000; \
+	(void) select (0, NULL, NULL, NULL, &_delay); \
+} while(0)
 #else
 #define PG_USLEEP(_usec) \
 do { \
 	SleepEx(((_usec) < 500 ? 1 : ((_usec) + 500) / 1000), TRUE); \
 } while(0)
+#define PG_MSLEEP(_msec) PG_USLEEP((_msec) * 1000)
 #endif
 
 #ifdef WIN32
@@ -209,6 +217,15 @@ extern bool enableFsync;
 extern bool allowSystemTableMods;
 extern DLLIMPORT int work_mem;
 extern DLLIMPORT int maintenance_work_mem;
+extern int	VacuumMem;
+
+extern int	VacuumCostPageHit;
+extern int	VacuumCostPageMiss;
+extern int	VacuumCostPageDirty;
+extern int	VacuumCostLimit;
+extern int	VacuumCostBalance;
+extern int	VacuumCostNaptime;
+extern bool	VacuumCostActive;
 
 /*
  *	A few postmaster startup options are exported here so the