diff -cprN head/src/backend/access/transam/xlog.c checkpoint/src/backend/access/transam/xlog.c
*** head/src/backend/access/transam/xlog.c	Mon Dec 11 13:52:47 2006
--- checkpoint/src/backend/access/transam/xlog.c	Fri Jan  5 14:15:58 2007
***************
*** 43,48 ****
--- 43,49 ----
  #include "storage/fd.h"
  #include "storage/pmsignal.h"
  #include "storage/procarray.h"
+ #include "storage/smgr.h"
  #include "storage/spin.h"
  #include "utils/builtins.h"
  #include "utils/nabstime.h"
*************** bool		XLOG_DEBUG = false;
*** 159,164 ****
--- 160,166 ----
  /* these are derived from XLOG_sync_method by assign_xlog_sync_method */
  int			sync_method = DEFAULT_SYNC_METHOD;
  static int	open_sync_bit = DEFAULT_SYNC_FLAGBIT;
+ int			checkpoint_nap_duration = 0;
  
  #define XLOG_SYNC_BIT  (enableFsync ? open_sync_bit : 0)
  
*************** static TransactionId recoveryStopXid;
*** 191,196 ****
--- 193,201 ----
  static time_t recoveryStopTime;
  static bool recoveryStopAfter;
  
+ /* checkpoint */
+ static CheckPoint checkPoint;
+ 
  /*
   * During normal operation, the only timeline we care about is ThisTimeLineID.
   * During recovery, however, things are more complicated.  To simplify life
*************** static void readRecoveryCommandFile(void
*** 469,475 ****
  static void exitArchiveRecovery(TimeLineID endTLI,
  					uint32 endLogId, uint32 endLogSeg);
  static bool recoveryStopsHere(XLogRecord *record, bool *includeThis);
! static void CheckPointGuts(XLogRecPtr checkPointRedo);
  
  static bool XLogCheckBuffer(XLogRecData *rdata, bool doPageWrites,
  				XLogRecPtr *lsn, BkpBlock *bkpb);
--- 474,480 ----
  static void exitArchiveRecovery(TimeLineID endTLI,
  					uint32 endLogId, uint32 endLogSeg);
  static bool recoveryStopsHere(XLogRecord *record, bool *includeThis);
! static void CheckPointGuts(XLogRecPtr checkPointRedo, bool flush);
  
  static bool XLogCheckBuffer(XLogRecData *rdata, bool doPageWrites,
  				XLogRecPtr *lsn, BkpBlock *bkpb);
*************** XLogWrite(XLogwrtRqst WriteRqst, bool fl
*** 1666,1672 ****
  						if (XLOG_DEBUG)
  							elog(LOG, "time for a checkpoint, signaling bgwriter");
  #endif
! 						RequestCheckpoint(false, true);
  					}
  				}
  			}
--- 1671,1677 ----
  						if (XLOG_DEBUG)
  							elog(LOG, "time for a checkpoint, signaling bgwriter");
  #endif
! 						RequestCheckpoint(false);
  					}
  				}
  			}
*************** GetRedoRecPtr(void)
*** 5343,5348 ****
--- 5348,5370 ----
  }
  
  /*
+  * GetInsertRecPtr -- Returns the current insert position.
+  */
+ XLogRecPtr
+ GetInsertRecPtr(void)
+ {
+ 	volatile XLogCtlData *xlogctl = XLogCtl;
+ 	XLogCtlInsert  *Insert = &XLogCtl->Insert;
+ 	XLogRecPtr		recptr;
+ 
+ 	SpinLockAcquire(&xlogctl->info_lck);
+ 	INSERT_RECPTR(recptr, Insert, Insert->curridx);
+ 	SpinLockRelease(&xlogctl->info_lck);
+ 
+ 	return recptr;
+ }
+ 
+ /*
   * Get the time of the last xlog segment switch
   */
  time_t
*************** ShutdownXLOG(int code, Datum arg)
*** 5417,5448 ****
  }
  
  /*
!  * Perform a checkpoint --- either during shutdown, or on-the-fly
   *
   * If force is true, we force a checkpoint regardless of whether any XLOG
   * activity has occurred since the last one.
   */
! void
! CreateCheckPoint(bool shutdown, bool force)
  {
- 	CheckPoint	checkPoint;
- 	XLogRecPtr	recptr;
  	XLogCtlInsert *Insert = &XLogCtl->Insert;
- 	XLogRecData rdata;
  	uint32		freespace;
- 	uint32		_logId;
- 	uint32		_logSeg;
- 	int			nsegsadded = 0;
- 	int			nsegsremoved = 0;
- 	int			nsegsrecycled = 0;
- 
- 	/*
- 	 * Acquire CheckpointLock to ensure only one checkpoint happens at a time.
- 	 * (This is just pro forma, since in the present system structure there is
- 	 * only one process that is allowed to issue checkpoints at any given
- 	 * time.)
- 	 */
- 	LWLockAcquire(CheckpointLock, LW_EXCLUSIVE);
  
  	/*
  	 * Use a critical section to force system panic if we have trouble.
--- 5439,5454 ----
  }
  
  /*
!  * PrepareCheckPoint -- prepare a checkpoint
   *
   * If force is true, we force a checkpoint regardless of whether any XLOG
   * activity has occurred since the last one.
   */
! bool
! PrepareCheckPoint(bool shutdown, bool force)
  {
  	XLogCtlInsert *Insert = &XLogCtl->Insert;
  	uint32		freespace;
  
  	/*
  	 * Use a critical section to force system panic if we have trouble.
*************** CreateCheckPoint(bool shutdown, bool for
*** 5501,5509 ****
  		{
  			LWLockRelease(WALInsertLock);
  			LWLockRelease(CheckpointStartLock);
- 			LWLockRelease(CheckpointLock);
  			END_CRIT_SECTION();
! 			return;
  		}
  	}
  
--- 5507,5515 ----
  		{
  			LWLockRelease(WALInsertLock);
  			LWLockRelease(CheckpointStartLock);
  			END_CRIT_SECTION();
! 			elog(DEBUG1, "checkpoint is not needed");
! 			return false;
  		}
  	}
  
*************** CreateCheckPoint(bool shutdown, bool for
*** 5552,5561 ****
  
  	LWLockRelease(CheckpointStartLock);
  
- 	if (!shutdown)
- 		ereport(DEBUG2,
- 				(errmsg("checkpoint starting")));
- 
  	/*
  	 * Get the other info we need for the checkpoint record.
  	 */
--- 5558,5563 ----
*************** CreateCheckPoint(bool shutdown, bool for
*** 5578,5583 ****
--- 5580,5625 ----
  							 &checkPoint.nextMulti,
  							 &checkPoint.nextMultiOffset);
  
+ 	END_CRIT_SECTION();
+ 
+ 	if (!shutdown)
+ 		ereport(DEBUG2,
+ 				(errmsg("checkpoint starting")));
+ 
+ 	return true;
+ }
+ 
+ /*
+  * CreateCheckPoint -- finish a checkpoint
+  */
+ void
+ CreateCheckPoint(bool shutdown, bool force)
+ {
+ 	XLogRecPtr	recptr;
+ 	XLogRecData rdata;
+ 	uint32		_logId;
+ 	uint32		_logSeg;
+ 	int			nsegsadded = 0;
+ 	int			nsegsremoved = 0;
+ 	int			nsegsrecycled = 0;
+ 
+ 	Assert(!shutdown || (shutdown && force));	/* must force on shutdown */
+ 
+ 	/* Do a force checkpoint if not prepared. (should not happen) */
+ 	if (!TransactionIdIsValid(checkPoint.nextXid))
+ 		force = true;
+ 
+ 	/*
+ 	 * Acquire CheckpointLock to ensure only one checkpoint happens at a time.
+ 	 * (This is just pro forma, since in the present system structure there is
+ 	 * only one process that is allowed to issue checkpoints at any given
+ 	 * time.)
+ 	 */
+ 	LWLockAcquire(CheckpointLock, LW_EXCLUSIVE);
+ 
+ 	if (shutdown || force)
+ 		PrepareCheckPoint(shutdown, force);
+ 
  	/*
  	 * Having constructed the checkpoint record, ensure all shmem disk buffers
  	 * and commit-log buffers are flushed to disk.
*************** CreateCheckPoint(bool shutdown, bool for
*** 5586,5594 ****
  	 * complete the checkpoint, but there is no reason to force a system
  	 * panic. Accordingly, exit critical section while doing it.
  	 */
! 	END_CRIT_SECTION();
! 
! 	CheckPointGuts(checkPoint.redo);
  
  	START_CRIT_SECTION();
  
--- 5628,5634 ----
  	 * complete the checkpoint, but there is no reason to force a system
  	 * panic. Accordingly, exit critical section while doing it.
  	 */
! 	CheckPointGuts(checkPoint.redo, force);
  
  	START_CRIT_SECTION();
  
*************** CreateCheckPoint(bool shutdown, bool for
*** 5684,5689 ****
--- 5724,5731 ----
  				(errmsg("checkpoint complete; %d transaction log file(s) added, %d removed, %d recycled",
  						nsegsadded, nsegsremoved, nsegsrecycled)));
  
+ 	checkPoint.nextXid = InvalidTransactionId;	/* done */
+ 
  	LWLockRelease(CheckpointLock);
  }
  
*************** CreateCheckPoint(bool shutdown, bool for
*** 5694,5705 ****
   * recovery restartpoints.
   */
  static void
! CheckPointGuts(XLogRecPtr checkPointRedo)
  {
  	CheckPointCLOG();
  	CheckPointSUBTRANS();
  	CheckPointMultiXact();
! 	FlushBufferPool();			/* performs all required fsyncs */
  	/* We deliberately delay 2PC checkpointing as long as possible */
  	CheckPointTwoPhase(checkPointRedo);
  }
--- 5736,5771 ----
   * recovery restartpoints.
   */
  static void
! CheckPointGuts(XLogRecPtr checkPointRedo, bool flush)
  {
  	CheckPointCLOG();
  	CheckPointSUBTRANS();
  	CheckPointMultiXact();
! 
! 	elog(DEBUG1, "CheckPointGuts %s flush", (flush ? "WITH" : "WITHOUT"));
! 
! 	if (flush)
! 		BufferSync();
! 	else if (checkpoint_nap_duration > 0)
! 	{
! 		/* naptime between write() and fsync() */
! 
! 		time_t	start;
! 
! 		elog(DEBUG1, "CHECKPOINT: nap between write() and fsync()");
! 		start = time(NULL);
! 		while (time(NULL) - start < checkpoint_nap_duration)
! 		{
! 			pg_usleep(BgWriterDelay * 1000L);
! 			BgBufferSync(0);
! 			AbsorbFsyncRequests();
! 		}
! 	}
! 
! 	elog(DEBUG1, "CHECKPOINT: fsync() start");
! 	smgrsync(flush);	/* performs all required fsyncs */
! 	elog(DEBUG1, "CHECKPOINT: fsync() finish");
! 
  	/* We deliberately delay 2PC checkpointing as long as possible */
  	CheckPointTwoPhase(checkPointRedo);
  }
*************** RecoveryRestartPoint(const CheckPoint *c
*** 5748,5754 ****
  	/*
  	 * OK, force data out to disk
  	 */
! 	CheckPointGuts(checkPoint->redo);
  
  	/*
  	 * Update pg_control so that any subsequent crash will restart from this
--- 5814,5820 ----
  	/*
  	 * OK, force data out to disk
  	 */
! 	CheckPointGuts(checkPoint->redo, true);
  
  	/*
  	 * Update pg_control so that any subsequent crash will restart from this
*************** pg_start_backup(PG_FUNCTION_ARGS)
*** 6167,6173 ****
  		 * have different checkpoint positions and hence different history
  		 * file names, even if nothing happened in between.
  		 */
! 		RequestCheckpoint(true, false);
  
  		/*
  		 * Now we need to fetch the checkpoint record location, and also its
--- 6233,6239 ----
  		 * have different checkpoint positions and hence different history
  		 * file names, even if nothing happened in between.
  		 */
! 		RequestCheckpoint(true);
  
  		/*
  		 * Now we need to fetch the checkpoint record location, and also its
diff -cprN head/src/backend/commands/dbcommands.c checkpoint/src/backend/commands/dbcommands.c
*** head/src/backend/commands/dbcommands.c	Tue Nov  7 09:40:50 2006
--- checkpoint/src/backend/commands/dbcommands.c	Tue Dec 19 11:12:15 2006
*************** createdb(const CreatedbStmt *stmt)
*** 504,510 ****
  		 * Perhaps if we ever implement CREATE DATABASE in a less cheesy way,
  		 * we can avoid this.
  		 */
! 		RequestCheckpoint(true, false);
  
  		/*
  		 * Close pg_database, but keep lock till commit (this is important to
--- 504,510 ----
  		 * Perhaps if we ever implement CREATE DATABASE in a less cheesy way,
  		 * we can avoid this.
  		 */
! 		RequestCheckpoint(true);
  
  		/*
  		 * Close pg_database, but keep lock till commit (this is important to
*************** dropdb(const char *dbname, bool missing_
*** 648,654 ****
  	 * open files, which would cause rmdir() to fail.
  	 */
  #ifdef WIN32
! 	RequestCheckpoint(true, false);
  #endif
  
  	/*
--- 648,654 ----
  	 * open files, which would cause rmdir() to fail.
  	 */
  #ifdef WIN32
! 	RequestCheckpoint(true);
  #endif
  
  	/*
diff -cprN head/src/backend/postmaster/bgwriter.c checkpoint/src/backend/postmaster/bgwriter.c
*** head/src/backend/postmaster/bgwriter.c	Mon Dec  4 16:08:41 2006
--- checkpoint/src/backend/postmaster/bgwriter.c	Fri Jan  5 17:21:26 2007
***************
*** 46,51 ****
--- 46,52 ----
  #include <signal.h>
  #include <time.h>
  #include <unistd.h>
+ #include <sys/time.h>
  
  #include "access/xlog_internal.h"
  #include "libpq/pqsignal.h"
***************
*** 90,99 ****
   *	6. If ckpt_failed is different from the originally saved value,
   *	   assume request failed; otherwise it was definitely successful.
   *
!  * An additional field is ckpt_time_warn; this is also sig_atomic_t for
   * simplicity, but is only used as a boolean.  If a backend is requesting
!  * a checkpoint for which a checkpoints-too-close-together warning is
!  * reasonable, it should set this field TRUE just before sending the signal.
   *
   * The requests array holds fsync requests sent by backends and not yet
   * absorbed by the bgwriter.  Unlike the checkpoint fields, the requests
--- 91,102 ----
   *	6. If ckpt_failed is different from the originally saved value,
   *	   assume request failed; otherwise it was definitely successful.
   *
!  * An additional field is ckpt_immediate; this is also sig_atomic_t for
   * simplicity, but is only used as a boolean.  If a backend is requesting
!  * an immediate checkpoint, set this field TRUE just before sending the
!  * signal. If do so, checkpoint is performed as soon as possible. Otherwise,
!  * checkpoint is run asynchronously to avoid i/o impacts and a checkpoints-
!  * too-close-together warning may be reported.
   *
   * The requests array holds fsync requests sent by backends and not yet
   * absorbed by the bgwriter.  Unlike the checkpoint fields, the requests
*************** typedef struct
*** 115,121 ****
  	sig_atomic_t ckpt_done;		/* advances when checkpoint done */
  	sig_atomic_t ckpt_failed;	/* advances when checkpoint fails */
  
! 	sig_atomic_t ckpt_time_warn;	/* warn if too soon since last ckpt? */
  
  	int			num_requests;	/* current # of requests */
  	int			max_requests;	/* allocated array size */
--- 118,124 ----
  	sig_atomic_t ckpt_done;		/* advances when checkpoint done */
  	sig_atomic_t ckpt_failed;	/* advances when checkpoint fails */
  
! 	sig_atomic_t ckpt_immediate;	/* immediate checkpoint requested? */
  
  	int			num_requests;	/* current # of requests */
  	int			max_requests;	/* allocated array size */
*************** static BgWriterShmemStruct *BgWriterShme
*** 130,135 ****
--- 133,139 ----
  int			BgWriterDelay = 200;
  int			CheckPointTimeout = 300;
  int			CheckPointWarning = 30;
+ int			checkpoint_write_duration = 0;
  
  /*
   * Flags set by interrupt handlers for later service in the main loop.
*************** static bool am_bg_writer = false;
*** 145,154 ****
--- 149,166 ----
  
  static bool ckpt_active = false;
  
+ /* Number of scanned pages during asynchronous checkpoint, otherwise -1. */
+ static int	ckpt_progress = -1;
+ 
+ /* WAL position at starting asynchronous checkpoint. */
+ static XLogRecPtr ckpt_recptr;
+ 
  static time_t last_checkpoint_time;
  static time_t last_xlog_switch_time;
  
  
+ static void BgCreateCheckPoint(bool immediate);
+ static int	BgGetScanPages(void);
  static void bg_quickdie(SIGNAL_ARGS);
  static void BgSigHupHandler(SIGNAL_ARGS);
  static void ReqCheckpointHandler(SIGNAL_ARGS);
*************** BackgroundWriterMain(void)
*** 285,290 ****
--- 297,305 ----
  			ckpt_active = false;
  		}
  
+ 		/* Discard prepared checkpoint */
+ 		ckpt_progress = -1;
+ 
  		/*
  		 * Now return to normal top-level context and clear ErrorContext for
  		 * next time.
*************** BackgroundWriterMain(void)
*** 327,333 ****
  	for (;;)
  	{
  		bool		do_checkpoint = false;
! 		bool		force_checkpoint = false;
  		time_t		now;
  		int			elapsed_secs;
  		long		udelay;
--- 342,348 ----
  	for (;;)
  	{
  		bool		do_checkpoint = false;
! 		bool		immediate = false;
  		time_t		now;
  		int			elapsed_secs;
  		long		udelay;
*************** BackgroundWriterMain(void)
*** 353,359 ****
  		{
  			checkpoint_requested = false;
  			do_checkpoint = true;
! 			force_checkpoint = true;
  		}
  		if (shutdown_requested)
  		{
--- 368,375 ----
  		{
  			checkpoint_requested = false;
  			do_checkpoint = true;
! 			immediate = BgWriterShmem->ckpt_immediate;
! 			BgWriterShmem->ckpt_immediate = false;
  		}
  		if (shutdown_requested)
  		{
*************** BackgroundWriterMain(void)
*** 385,423 ****
  		if (do_checkpoint)
  		{
  			/*
- 			 * We will warn if (a) too soon since last checkpoint (whatever
- 			 * caused it) and (b) somebody has set the ckpt_time_warn flag
- 			 * since the last checkpoint start.  Note in particular that this
- 			 * implementation will not generate warnings caused by
- 			 * CheckPointTimeout < CheckPointWarning.
- 			 */
- 			if (BgWriterShmem->ckpt_time_warn &&
- 				elapsed_secs < CheckPointWarning)
- 				ereport(LOG,
- 						(errmsg("checkpoints are occurring too frequently (%d seconds apart)",
- 								elapsed_secs),
- 						 errhint("Consider increasing the configuration parameter \"checkpoint_segments\".")));
- 			BgWriterShmem->ckpt_time_warn = false;
- 
- 			/*
  			 * Indicate checkpoint start to any waiting backends.
  			 */
  			ckpt_active = true;
  			BgWriterShmem->ckpt_started++;
  
! 			CreateCheckPoint(false, force_checkpoint);
! 
! 			/*
! 			 * After any checkpoint, close all smgr files.	This is so we
! 			 * won't hang onto smgr references to deleted files indefinitely.
! 			 */
! 			smgrcloseall();
  
! 			/*
! 			 * Indicate checkpoint completion to any waiting backends.
! 			 */
! 			BgWriterShmem->ckpt_done = BgWriterShmem->ckpt_started;
! 			ckpt_active = false;
  
  			/*
  			 * Note we record the checkpoint start time not end time as
--- 401,456 ----
  		if (do_checkpoint)
  		{
  			/*
  			 * Indicate checkpoint start to any waiting backends.
  			 */
  			ckpt_active = true;
  			BgWriterShmem->ckpt_started++;
  
! 			if (immediate)
! 			{
! 				/* Cancel previous asynchronous checkpoint if running */
! 				if (ckpt_progress >= 0)
! 				{
! 					elog(DEBUG1, "cancel previous asynchronous checkpoint");
! 					ckpt_progress = -1;
! 				}
! 				BgCreateCheckPoint(true);
! 			}
! 			else
! 			{
! 				/*
! 				 * We will warn if (a) too soon since last checkpoint
! 				 * (whatever caused it) and (b) the checkpoint is not forced.
! 				 * Note in particular that this implementation will not generate
! 				 * warnings caused by CheckPointTimeout < CheckPointWarning.
! 				 */
! 				if (elapsed_secs < CheckPointTimeout &&
! 					elapsed_secs < CheckPointWarning)
! 					ereport(LOG,
! 						(errmsg("checkpoints are occurring too frequently (%d seconds apart)",
! 								elapsed_secs),
! 						 errhint("Consider increasing the configuration parameter \"checkpoint_segments\".")));
  
! 				/* Finish previous asynchronous checkpoint if running. */
! 				if (ckpt_progress >= 0)
! 				{
! 					int	num_to_scan = NBuffers - ckpt_progress;
! 					if (num_to_scan > 0)
! 					{
! 						elog(DEBUG1, "finish previous checkpoint, writing %d buffers", num_to_scan);
! 						BgBufferSync(num_to_scan);
! 					}
! 					BgCreateCheckPoint(false);
! 				}
! 
! 				/* Start an asynchronous checkpoint */
! 				elog(DEBUG1, "prepare asynchronous checkpoint");
! 				if (PrepareCheckPoint(false, false))
! 				{
! 					ckpt_progress = 0;	/* >=0 means 'prepared' */
! 					ckpt_recptr = GetInsertRecPtr();
! 				}
! 			}
  
  			/*
  			 * Note we record the checkpoint start time not end time as
*************** BackgroundWriterMain(void)
*** 426,433 ****
  			 */
  			last_checkpoint_time = now;
  		}
  		else
! 			BgBufferSync();
  
  		/*
  		 * Check for archive_timeout, if so, switch xlog files.  First we do a
--- 459,481 ----
  			 */
  			last_checkpoint_time = now;
  		}
+ 		else if (ckpt_progress >= 0)
+ 		{
+ 			/* asynchronous checkpoint is running */
+ 			ckpt_progress += BgBufferSync(BgGetScanPages());
+ 
+ 			/*
+ 			 * If all dirty buffers have been written, we can continue
+ 			 * the rest of the checkpoint.
+ 			 */
+ 			if (ckpt_progress >= NBuffers)
+ 				BgCreateCheckPoint(false);
+ 		}
  		else
! 		{
! 			/* Normal activity, not in checkpoints */
! 			BgBufferSync(0);
! 		}
  
  		/*
  		 * Check for archive_timeout, if so, switch xlog files.  First we do a
*************** BackgroundWriterMain(void)
*** 506,511 ****
--- 554,628 ----
  	}
  }
  
+ /*
+  * BgCreateCheckPoint -- create checkpoint and close all smgr files.
+  */
+ static void
+ BgCreateCheckPoint(bool immediate)
+ {
+ 	elog(DEBUG1, "start %s checkpoint",
+ 		(immediate ? "immediate" : "asynchronous"));
+ 
+ 	ckpt_active = true;
+ 
+ 	CreateCheckPoint(false, immediate);
+ 
+ 	/*
+ 	 * After any checkpoint, close all smgr files.	This is so we
+ 	 * won't hang onto smgr references to deleted files indefinitely.
+ 	 */
+ 	smgrcloseall();
+ 
+ 	/*
+ 	 * Indicate checkpoint completion to any waiting backends.
+ 	 */
+ 	BgWriterShmem->ckpt_done = BgWriterShmem->ckpt_started;
+ 
+ 	ckpt_progress = -1;
+ 	ckpt_active = false;
+ }
+ 
+ /*
+  * BgGetScanPages -- Returns the number of pages recommemded to be scanned.
+  */
+ static int
+ BgGetScanPages(void)
+ {
+ 	struct timeval	now;
+ 	XLogRecPtr		recptr;
+ 	double			progress_in_time,
+ 					progress_in_xlog;
+ 	int				pos;
+ 
+ 	if (checkpoint_write_duration == 0)
+ 		return NBuffers;	/* write all pages at once */
+ 
+ 	/* coordinate the progress with checkpoint_timeout */
+ 	gettimeofday(&now, NULL);
+ 	progress_in_time = ((double)(int)(now.tv_sec - last_checkpoint_time) +
+ 						now.tv_usec / 1000000.0) / checkpoint_write_duration;
+ 
+ 	/* coordinate the progress with checkpoint_segments */
+ 	recptr = GetInsertRecPtr();
+ 	progress_in_xlog =
+ 		((double) (recptr.xlogid - ckpt_recptr.xlogid) * XLogSegsPerFile +
+ 		 (double) (recptr.xrecoff - ckpt_recptr.xrecoff) / XLogSegSize) /
+ 		CheckPointSegments;
+ 
+ 	pos = (int) (NBuffers * Max(progress_in_time, progress_in_xlog));
+ 	if (pos > NBuffers)
+ 		pos = NBuffers;
+ 
+ 	elog(DEBUG2, "checkpoint progress : %d->%d(%+d) (time=%.3f, xlog=%.3f)",
+ 		ckpt_progress, pos, pos - ckpt_progress,
+ 		progress_in_time, progress_in_xlog);
+ 
+ 	if (pos > ckpt_progress)
+ 		return pos - ckpt_progress;
+ 	else
+ 		return 0;
+ }
+ 
  
  /* --------------------------------
   *		signal handler routines
*************** BgWriterShmemInit(void)
*** 612,625 ****
   *
   * If waitforit is true, wait until the checkpoint is completed
   * before returning; otherwise, just signal the request and return
!  * immediately.
!  *
!  * If warnontime is true, and it's "too soon" since the last checkpoint,
!  * the bgwriter will log a warning.  This should be true only for checkpoints
!  * caused due to xlog filling, else the warning will be misleading.
   */
  void
! RequestCheckpoint(bool waitforit, bool warnontime)
  {
  	/* use volatile pointer to prevent code rearrangement */
  	volatile BgWriterShmemStruct *bgs = BgWriterShmem;
--- 729,740 ----
   *
   * If waitforit is true, wait until the checkpoint is completed
   * before returning; otherwise, just signal the request and return
!  * immediately. If false, checkpoints are caused due to xlog filling.
!  * Then if it's "too soon" since the last checkpoint, the bgwriter will
!  * log a warning, else the warning will be misleading.
   */
  void
! RequestCheckpoint(bool waitforit)
  {
  	/* use volatile pointer to prevent code rearrangement */
  	volatile BgWriterShmemStruct *bgs = BgWriterShmem;
*************** RequestCheckpoint(bool waitforit, bool w
*** 642,650 ****
  		return;
  	}
  
! 	/* Set warning request flag if appropriate */
! 	if (warnontime)
! 		bgs->ckpt_time_warn = true;
  
  	/*
  	 * Send signal to request checkpoint.  When waitforit is false, we
--- 757,765 ----
  		return;
  	}
  
! 	/* Set immediate request flag if appropriate */
! 	if (waitforit)
! 		bgs->ckpt_immediate = true;
  
  	/*
  	 * Send signal to request checkpoint.  When waitforit is false, we
diff -cprN head/src/backend/storage/buffer/bufmgr.c checkpoint/src/backend/storage/buffer/bufmgr.c
*** head/src/backend/storage/buffer/bufmgr.c	Thu Oct 26 12:48:40 2006
--- checkpoint/src/backend/storage/buffer/bufmgr.c	Fri Jan  5 11:26:35 2007
*************** BufferSync(void)
*** 985,1005 ****
   * BgBufferSync -- Write out some dirty buffers in the pool.
   *
   * This is called periodically by the background writer process.
   */
! void
! BgBufferSync(void)
  {
  	static int	buf_id1 = 0;
  	int			buf_id2;
- 	int			num_to_scan;
  	int			num_written;
  
  	/* Make sure we can handle the pin inside SyncOneBuffer */
  	ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
  
  	/*
! 	 * To minimize work at checkpoint time, we want to try to keep all the
! 	 * buffers clean; this motivates a scan that proceeds sequentially through
  	 * all buffers.  But we are also charged with ensuring that buffers that
  	 * will be recycled soon are clean when needed; these buffers are the ones
  	 * just ahead of the StrategySyncStart point.  We make a separate scan
--- 985,1009 ----
   * BgBufferSync -- Write out some dirty buffers in the pool.
   *
   * This is called periodically by the background writer process.
+  * num_to_scan buffers are always scanned and written by the 'all' activity.
+  * Returns the number of scanned buffers by the 'all' activity.
   */
! int
! BgBufferSync(int num_to_scan)
  {
  	static int	buf_id1 = 0;
  	int			buf_id2;
  	int			num_written;
+ 	int			num_scanned = 0;
+ 
+ 	Assert(num_to_scan >= 0);
  
  	/* Make sure we can handle the pin inside SyncOneBuffer */
  	ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
  
  	/*
! 	 * To distribute work at checkpoint time, we want to try to write buffers
! 	 * by increments; this motivates a scan that proceeds sequentially through
  	 * all buffers.  But we are also charged with ensuring that buffers that
  	 * will be recycled soon are clean when needed; these buffers are the ones
  	 * just ahead of the StrategySyncStart point.  We make a separate scan
*************** BgBufferSync(void)
*** 1007,1027 ****
  	 */
  
  	/*
! 	 * This loop runs over all buffers, including pinned ones.	The starting
! 	 * point advances through the buffer pool on successive calls.
  	 *
  	 * Note that we advance the static counter *before* trying to write. This
  	 * ensures that, if we have a persistent write failure on a dirty buffer,
  	 * we'll still be able to make progress writing other buffers. (The
  	 * bgwriter will catch the error and just call us again later.)
  	 */
! 	if (bgwriter_all_percent > 0.0 && bgwriter_all_maxpages > 0)
  	{
! 		num_to_scan = (int) ((NBuffers * bgwriter_all_percent + 99) / 100);
! 		num_written = 0;
  
  		while (num_to_scan-- > 0)
  		{
  			if (++buf_id1 >= NBuffers)
  				buf_id1 = 0;
  			if (SyncOneBuffer(buf_id1, false))
--- 1011,1043 ----
  	 */
  
  	/*
! 	 * These two loops run over all buffers, including pinned ones.	The
! 	 * starting point advances through the buffer pool on successive calls.
  	 *
  	 * Note that we advance the static counter *before* trying to write. This
  	 * ensures that, if we have a persistent write failure on a dirty buffer,
  	 * we'll still be able to make progress writing other buffers. (The
  	 * bgwriter will catch the error and just call us again later.)
  	 */
! 
! 	num_written = 0;
! 	while (num_to_scan-- > 0)
  	{
! 		num_scanned += 1;
! 		if (++buf_id1 >= NBuffers)
! 			buf_id1 = 0;
! 		if (SyncOneBuffer(buf_id1, false))
! 			++num_written;
! 	}
! 
! 	if (bgwriter_all_percent > 0.0 && bgwriter_all_maxpages > num_written)
! 	{
! 		num_to_scan = (int) ((NBuffers * bgwriter_all_percent + 99) / 100)
! 					- num_scanned;
  
  		while (num_to_scan-- > 0)
  		{
+ 			num_scanned += 1;
  			if (++buf_id1 >= NBuffers)
  				buf_id1 = 0;
  			if (SyncOneBuffer(buf_id1, false))
*************** BgBufferSync(void)
*** 1054,1059 ****
--- 1070,1077 ----
  				buf_id2 = 0;
  		}
  	}
+ 
+ 	return num_scanned;
  }
  
  /*
*************** void
*** 1273,1279 ****
  FlushBufferPool(void)
  {
  	BufferSync();
! 	smgrsync();
  }
  
  
--- 1291,1297 ----
  FlushBufferPool(void)
  {
  	BufferSync();
! 	smgrsync(true);
  }
  
  
diff -cprN head/src/backend/storage/file/fd.c checkpoint/src/backend/storage/file/fd.c
*** head/src/backend/storage/file/fd.c	Wed Nov  8 14:10:42 2006
--- checkpoint/src/backend/storage/file/fd.c	Fri Jan  5 17:21:26 2007
***************
*** 40,51 ****
--- 40,59 ----
  
  #include "postgres.h"
  
+ #ifndef _GNU_SOURCE
+ #define _GNU_SOURCE
+ #endif
+ 
  #include <sys/file.h>
  #include <sys/param.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #include <fcntl.h>
  
+ #ifdef _POSIX_MAPPED_FILES
+ #include <sys/mman.h>
+ #endif
+ 
  #include "miscadmin.h"
  #include "access/xact.h"
  #include "storage/fd.h"
*************** FileSync(File file)
*** 1122,1127 ****
--- 1130,1190 ----
  	return pg_fsync(VfdCache[file].fd);
  }
  
+ int
+ FileSyncRange(File file, long offset, long amount)
+ {
+ 	int			returnCode;
+ 
+ 	Assert(FileIsValid(file));
+ 
+ 	DO_DB(elog(LOG, "FileSyncRange: %d (%s) %ld %ld",
+ 			   file, VfdCache[file].fileName, offset, amount));
+ 
+ 	returnCode = FileAccess(file);
+ 	if (returnCode < 0)
+ 		return returnCode;
+ 
+ #if defined(SYNC_FILE_RANGE_WRITE)
+ 	returnCode = sync_file_range(VfdCache[file].fd, offset, amount,
+ 		SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER);
+ #elif _POSIX_MAPPED_FILES > 0
+ 	{
+ 		void *p = mmap(NULL, amount, PROT_READ | PROT_WRITE, MAP_SHARED,
+ 			VfdCache[file].fd, offset);
+ 		if (p == MAP_FAILED)
+ 			return -1;
+ 		returnCode = msync(p, amount, MS_SYNC);
+ 		munmap(p, amount);
+ 	}
+ #elif defined(WIN32)
+ 	{
+ 		HANDLE	mapping = NULL;
+ 		LPVOID	p = NULL;
+ 
+ 		if (!(mapping = CreateFileMapping(
+ 					(HANDLE) _get_osfhandle(VfdCache[file].fd),
+ 					NULL, PAGE_READWRITE, 0, 0, NULL)) ||
+ 			!(p = MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS,
+ 					0, offset, amount)) ||
+ 			!FlushViewOfFile(p, amount))
+ 		{
+ 			returnCode = -1;
+ 			errno = GetLastError();
+ 		}
+ 
+ 		if (p)
+ 			UnmapViewOfFile(p);
+ 		if (mapping)
+ 			CloseHandle(mapping);
+ 	}
+ #else
+ 	elog(WARNING, "Native sync_file_range() is not supported");
+ 	returnCode = pg_fsync(VfdCache[file].fd);
+ #endif
+ 
+ 	return returnCode;
+ }
+ 
  long
  FileSeek(File file, long offset, int whence)
  {
diff -cprN head/src/backend/storage/smgr/md.c checkpoint/src/backend/storage/smgr/md.c
*** head/src/backend/storage/smgr/md.c	Fri Jan  5 15:25:19 2007
--- checkpoint/src/backend/storage/smgr/md.c	Fri Jan  5 17:21:26 2007
***************
*** 17,26 ****
--- 17,28 ----
  #include <unistd.h>
  #include <fcntl.h>
  #include <sys/file.h>
+ #include <sys/time.h>
  
  #include "catalog/catalog.h"
  #include "miscadmin.h"
  #include "postmaster/bgwriter.h"
+ #include "storage/bufmgr.h"
  #include "storage/fd.h"
  #include "storage/bufmgr.h"
  #include "storage/smgr.h"
*************** typedef struct _MdfdVec
*** 86,92 ****
  } MdfdVec;
  
  static MemoryContext MdCxt;		/* context for all md.c allocations */
! 
  
  /*
   * In some contexts (currently, standalone backends and the bgwriter process)
--- 88,95 ----
  } MdfdVec;
  
  static MemoryContext MdCxt;		/* context for all md.c allocations */
! int		checkpoint_sync_size = 0;
! int		checkpoint_sync_duration = 0;
  
  /*
   * In some contexts (currently, standalone backends and the bgwriter process)
*************** mdimmedsync(SMgrRelation reln)
*** 825,830 ****
--- 828,856 ----
  #endif
  }
  
+ static void
+ sleep_between_sync(long len, uint64 total_len)
+ {
+ 	struct timeval start;
+ 	struct timeval now;
+ 	uint64	usec;
+ 
+ 	if (len <= 0 || total_len == 0 || checkpoint_sync_duration == 0)
+ 		return;
+ 
+ 	usec = (uint64) checkpoint_sync_duration * 1000000 * len / total_len;
+ 
+ 	gettimeofday(&start, NULL);
+ 	do
+ 	{
+ 		pg_usleep(BgWriterDelay * 1000L);
+ 		BgBufferSync(0);
+ 		AbsorbFsyncRequests();
+ 		gettimeofday(&now, NULL);
+ 	} while((uint64) (now.tv_sec - start.tv_sec) * 1000000 +
+ 			(now.tv_usec - start.tv_usec) < usec);
+ }
+ 
  /*
   *	mdsync() -- Sync previous writes to stable storage.
   *
*************** mdsync(void)
*** 837,842 ****
--- 863,869 ----
  	HASH_SEQ_STATUS hstat;
  	PendingOperationEntry *entry;
  	int			absorb_counter;
+ 	uint64		total_len;
  
  	if (!pendingOpsTable)
  		elog(ERROR, "cannot sync without a pendingOpsTable");
*************** mdsync(void)
*** 849,854 ****
--- 876,903 ----
  	 */
  	AbsorbFsyncRequests();
  
+ 	/*
+ 	 * Calc the total length of files to fsync.
+ 	 */
+ 	total_len = 0;
+ 	if (checkpoint_sync_duration > 0)
+ 	{
+ 		hash_seq_init(&hstat, pendingOpsTable);
+ 		while ((entry = (PendingOperationEntry *) hash_seq_search(&hstat)) != NULL)
+ 		{
+ 			SMgrRelation	reln;
+ 			MdfdVec		   *seg;
+ 			long			len;
+ 
+ 			reln = smgropen(entry->rnode);
+ 			seg = _mdfd_getseg(reln,
+ 							entry->segno * ((BlockNumber) RELSEG_SIZE),
+ 							true, EXTENSION_RETURN_NULL);
+ 			if (seg && (len = FileSeek(seg->mdfd_vfd, 0, SEEK_END)) >= 0)
+ 				total_len += len;
+ 		}
+ 	}
+ 
  	absorb_counter = FSYNCS_PER_ABSORB;
  	hash_seq_init(&hstat, pendingOpsTable);
  	while ((entry = (PendingOperationEntry *) hash_seq_search(&hstat)) != NULL)
*************** mdsync(void)
*** 905,919 ****
  							   false, EXTENSION_RETURN_NULL);
  			if (seg)
  			{
! 				if (FileSync(seg->mdfd_vfd) < 0 &&
! 					errno != ENOENT)
! 					ereport(ERROR,
  							(errcode_for_file_access(),
  							 errmsg("could not fsync segment %u of relation %u/%u/%u: %m",
  									entry->segno,
  									entry->rnode.spcNode,
  									entry->rnode.dbNode,
  									entry->rnode.relNode)));
  			}
  		}
  
--- 954,1011 ----
  							   false, EXTENSION_RETURN_NULL);
  			if (seg)
  			{
! 				if (checkpoint_sync_size > 0 && checkpoint_sync_duration > 0)
! 				{
! 					long	len;
! 					long	off;
! 					long	sync_bytes = checkpoint_sync_size * 1024;
! 
! 					len = FileSeek(seg->mdfd_vfd, 0, SEEK_END);
! 					for (off = 0; off < len; off += sync_bytes)
! 					{
! 						long	amount;
! 
! 						if (off + sync_bytes < len)
! 							amount = sync_bytes;
! 						else
! 							amount = len - off;
! 
! 						elog(DEBUG2, "FileSyncRange : segment %u of relation %u/%u/%u range %ld/%ld",
! 							entry->segno,
! 							entry->rnode.spcNode,
! 							entry->rnode.dbNode,
! 							entry->rnode.relNode,
! 							off, amount);
! 
! 						if (FileSyncRange(seg->mdfd_vfd, off, amount) < 0 && errno != ENOENT)
! 							ereport(ERROR,
! 								(errcode_for_file_access(),
! 								 errmsg("could not fsync segment %u of relation %u/%u/%u range %ld/%ld: %m",
! 									entry->segno,
! 									entry->rnode.spcNode,
! 									entry->rnode.dbNode,
! 									entry->rnode.relNode,
! 									off, amount)));
! 
! 						if (total_len > 0)
! 							sleep_between_sync(amount, total_len);
! 					}
! 				}
! 				else
! 				{
! 					if (FileSync(seg->mdfd_vfd) < 0 && errno != ENOENT)
! 						ereport(ERROR,
  							(errcode_for_file_access(),
  							 errmsg("could not fsync segment %u of relation %u/%u/%u: %m",
  									entry->segno,
  									entry->rnode.spcNode,
  									entry->rnode.dbNode,
  									entry->rnode.relNode)));
+ 
+ 					if (total_len > 0)
+ 						sleep_between_sync(
+ 							FileSeek(seg->mdfd_vfd, 0, SEEK_END), total_len);
+ 				}
  			}
  		}
  
diff -cprN head/src/backend/storage/smgr/smgr.c checkpoint/src/backend/storage/smgr/smgr.c
*** head/src/backend/storage/smgr/smgr.c	Fri Jan  5 15:25:19 2007
--- checkpoint/src/backend/storage/smgr/smgr.c	Fri Jan  5 15:28:49 2007
***************
*** 27,32 ****
--- 27,33 ----
  #include "storage/smgr.h"
  #include "utils/memutils.h"
  
+ extern int	checkpoint_sync_duration;
  
  /*
   * This struct of function pointers defines the API between smgr.c and
*************** smgrabort(void)
*** 781,795 ****
   *	smgrsync() -- Sync files to disk at checkpoint time.
   */
  void
! smgrsync(void)
  {
  	int			i;
  
  	for (i = 0; i < NSmgr; i++)
  	{
  		if (smgrsw[i].smgr_sync)
  			(*(smgrsw[i].smgr_sync)) ();
  	}
  }
  
  
--- 782,804 ----
   *	smgrsync() -- Sync files to disk at checkpoint time.
   */
  void
! smgrsync(bool immediate)
  {
  	int			i;
+ 	long	prev_checkpoint_sync_duration = checkpoint_sync_duration;
+ 
+ 	/* FIXME: Use more clever ways. */
+ 	if (immediate)
+ 		checkpoint_sync_duration = 0;
  
  	for (i = 0; i < NSmgr; i++)
  	{
  		if (smgrsw[i].smgr_sync)
  			(*(smgrsw[i].smgr_sync)) ();
  	}
+ 
+ 	if (immediate)
+ 		checkpoint_sync_duration = prev_checkpoint_sync_duration;
  }
  
  
diff -cprN head/src/backend/tcop/utility.c checkpoint/src/backend/tcop/utility.c
*** head/src/backend/tcop/utility.c	Tue Oct 17 18:18:32 2006
--- checkpoint/src/backend/tcop/utility.c	Tue Dec 19 11:12:15 2006
*************** ProcessUtility(Node *parsetree,
*** 1055,1061 ****
  				ereport(ERROR,
  						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  						 errmsg("must be superuser to do CHECKPOINT")));
! 			RequestCheckpoint(true, false);
  			break;
  
  		case T_ReindexStmt:
--- 1055,1061 ----
  				ereport(ERROR,
  						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  						 errmsg("must be superuser to do CHECKPOINT")));
! 			RequestCheckpoint(true);
  			break;
  
  		case T_ReindexStmt:
diff -cprN head/src/backend/utils/misc/guc.c checkpoint/src/backend/utils/misc/guc.c
*** head/src/backend/utils/misc/guc.c	Thu Jan  4 14:56:19 2007
--- checkpoint/src/backend/utils/misc/guc.c	Fri Jan  5 17:21:26 2007
*************** extern int	CommitDelay;
*** 99,104 ****
--- 99,108 ----
  extern int	CommitSiblings;
  extern char *default_tablespace;
  extern bool fullPageWrites;
+ extern int	checkpoint_write_duration;
+ extern int	checkpoint_nap_duration;
+ extern int	checkpoint_sync_duration;
+ extern int	checkpoint_sync_size;
  
  #ifdef TRACE_SORT
  extern bool trace_sort;
*************** static struct config_int ConfigureNamesI
*** 1423,1428 ****
--- 1427,1472 ----
  	},
  
  	{
+ 		{"checkpoint_write_duration", PGC_SIGHUP, WAL_CHECKPOINTS,
+ 			gettext_noop("Sets the duration of write() phase in checkpoints."),
+ 			NULL,
+ 			GUC_UNIT_S
+ 		},
+ 		&checkpoint_write_duration,
+ 		0, 0, 3600, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"checkpoint_nap_duration", PGC_SIGHUP, WAL_CHECKPOINTS,
+ 			gettext_noop("Sets the duration between write() and fsync() phases in checkpoints."),
+ 			NULL,
+ 			GUC_UNIT_S
+ 		},
+ 		&checkpoint_nap_duration,
+ 		0, 0, 3600, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"checkpoint_sync_duration", PGC_SIGHUP, WAL_CHECKPOINTS,
+ 			gettext_noop("Sets the duration of fsync() phase in checkpoints."),
+ 			NULL,
+ 			GUC_UNIT_S
+ 		},
+ 		&checkpoint_sync_duration,
+ 		0, 0, 3600, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"checkpoint_sync_size", PGC_SIGHUP, WAL_CHECKPOINTS,
+ 			gettext_noop("Sets the synchronization unit of data files in checkpoints."),
+ 			NULL,
+ 			GUC_UNIT_KB
+ 		},
+ 		&checkpoint_sync_size,
+ 		0, 0, RELSEG_SIZE * BLCKSZ / 1024, NULL, NULL
+ 	},
+ 
+ 	{
  		{"wal_buffers", PGC_POSTMASTER, WAL_SETTINGS,
  			gettext_noop("Sets the number of disk-page buffers in shared memory for WAL."),
  			NULL,
diff -cprN head/src/backend/utils/misc/postgresql.conf.sample checkpoint/src/backend/utils/misc/postgresql.conf.sample
*** head/src/backend/utils/misc/postgresql.conf.sample	Wed Nov 29 15:21:18 2006
--- checkpoint/src/backend/utils/misc/postgresql.conf.sample	Fri Jan  5 11:26:35 2007
***************
*** 164,169 ****
--- 164,173 ----
  #checkpoint_segments = 3		# in logfile segments, min 1, 16MB each
  #checkpoint_timeout = 5min		# range 30s-1h
  #checkpoint_warning = 30s		# 0 is off
+ #checkpoint_write_duration = 0s		# duration in write() phase in checkpoints
+ #checkpoint_nap_duration = 0s		# duration between write() and fsync()
+ #checkpoint_sync_duration = 0s		# duration in fsync() phase in checkpoints
+ #checkpoint_sync_size = 0MB		# synchronization unit of files, 0 is at once
  
  # - Archiving -
  
diff -cprN head/src/include/access/xlog.h checkpoint/src/include/access/xlog.h
*** head/src/include/access/xlog.h	Tue Nov  7 09:40:50 2006
--- checkpoint/src/include/access/xlog.h	Tue Dec 19 11:12:15 2006
*************** extern void BootStrapXLOG(void);
*** 162,170 ****
--- 162,172 ----
  extern void StartupXLOG(void);
  extern void ShutdownXLOG(int code, Datum arg);
  extern void InitXLOGAccess(void);
+ extern bool PrepareCheckPoint(bool shutdown, bool force);
  extern void CreateCheckPoint(bool shutdown, bool force);
  extern void XLogPutNextOid(Oid nextOid);
  extern XLogRecPtr GetRedoRecPtr(void);
+ extern XLogRecPtr GetInsertRecPtr(void);
  extern void GetNextXidAndEpoch(TransactionId *xid, uint32 *epoch);
  
  #endif   /* XLOG_H */
diff -cprN head/src/include/postmaster/bgwriter.h checkpoint/src/include/postmaster/bgwriter.h
*** head/src/include/postmaster/bgwriter.h	Tue Oct 10 16:16:01 2006
--- checkpoint/src/include/postmaster/bgwriter.h	Tue Dec 19 11:12:15 2006
*************** extern int	CheckPointWarning;
*** 23,29 ****
  
  extern void BackgroundWriterMain(void);
  
! extern void RequestCheckpoint(bool waitforit, bool warnontime);
  
  extern bool ForwardFsyncRequest(RelFileNode rnode, BlockNumber segno);
  extern void AbsorbFsyncRequests(void);
--- 23,29 ----
  
  extern void BackgroundWriterMain(void);
  
! extern void RequestCheckpoint(bool waitforit);
  
  extern bool ForwardFsyncRequest(RelFileNode rnode, BlockNumber segno);
  extern void AbsorbFsyncRequests(void);
diff -cprN head/src/include/storage/bufmgr.h checkpoint/src/include/storage/bufmgr.h
*** head/src/include/storage/bufmgr.h	Tue Oct 10 16:16:01 2006
--- checkpoint/src/include/storage/bufmgr.h	Tue Dec 19 11:12:15 2006
*************** extern void AbortBufferIO(void);
*** 151,157 ****
  
  extern void BufmgrCommit(void);
  extern void BufferSync(void);
! extern void BgBufferSync(void);
  
  extern void AtProcExit_LocalBuffers(void);
  
--- 151,157 ----
  
  extern void BufmgrCommit(void);
  extern void BufferSync(void);
! extern int	BgBufferSync(int num_to_scan);
  
  extern void AtProcExit_LocalBuffers(void);
  
diff -cprN head/src/include/storage/fd.h checkpoint/src/include/storage/fd.h
*** head/src/include/storage/fd.h	Tue Oct 10 16:16:01 2006
--- checkpoint/src/include/storage/fd.h	Fri Jan  5 11:26:35 2007
*************** extern void FileUnlink(File file);
*** 67,72 ****
--- 67,73 ----
  extern int	FileRead(File file, char *buffer, int amount);
  extern int	FileWrite(File file, char *buffer, int amount);
  extern int	FileSync(File file);
+ extern int	FileSyncRange(File file, long offset, long amount);
  extern long FileSeek(File file, long offset, int whence);
  extern int	FileTruncate(File file, long offset);
  
diff -cprN head/src/include/storage/smgr.h checkpoint/src/include/storage/smgr.h
*** head/src/include/storage/smgr.h	Fri Jan  5 15:25:19 2007
--- checkpoint/src/include/storage/smgr.h	Fri Jan  5 15:28:49 2007
*************** extern void AtSubAbort_smgr(void);
*** 82,88 ****
  extern void PostPrepare_smgr(void);
  extern void smgrcommit(void);
  extern void smgrabort(void);
! extern void smgrsync(void);
  
  extern void smgr_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void smgr_desc(StringInfo buf, uint8 xl_info, char *rec);
--- 82,88 ----
  extern void PostPrepare_smgr(void);
  extern void smgrcommit(void);
  extern void smgrabort(void);
! extern void smgrsync(bool immediate);
  
  extern void smgr_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void smgr_desc(StringInfo buf, uint8 xl_info, char *rec);
