Commit ec29427c authored by Tom Lane's avatar Tom Lane

Fix and simplify some usages of TimestampDifference().

Introduce TimestampDifferenceMilliseconds() to simplify callers
that would rather have the difference in milliseconds, instead of
the select()-oriented seconds-and-microseconds format.  This gets
rid of at least one integer division per call, and it eliminates
some apparently-easy-to-mess-up arithmetic.

Two of these call sites were in fact wrong:

* pg_prewarm's autoprewarm_main() forgot to multiply the seconds
by 1000, thus ending up with a delay 1000X shorter than intended.
That doesn't quite make it a busy-wait, but close.

* postgres_fdw's pgfdw_get_cleanup_result() thought it needed to compute
microseconds not milliseconds, thus ending up with a delay 1000X longer
than intended.  Somebody along the way had noticed this problem but
misdiagnosed the cause, and imposed an ad-hoc 60-second limit rather
than fixing the units.  This was relatively harmless in context, because
we don't care that much about exactly how long this delay is; still,
it's wrong.

There are a few more callers of TimestampDifference() that don't
have a direct need for seconds-and-microseconds, but can't use
TimestampDifferenceMilliseconds() either because they do need
microsecond precision or because they might possibly deal with
intervals long enough to overflow 32-bit milliseconds.  It might be
worth inventing another API to improve that, but that seems outside
the scope of this patch; so those callers are untouched here.

Given the fact that we are fixing some bugs, and the likelihood
that future patches might want to back-patch code that uses this
new API, back-patch to all supported branches.

Alexey Kondratov and Tom Lane

Discussion: https://postgr.es/m/3b1c053a21c07c1ed5e00be3b2b855ef@postgrespro.ru
parent b8b6a012
...@@ -220,18 +220,16 @@ autoprewarm_main(Datum main_arg) ...@@ -220,18 +220,16 @@ autoprewarm_main(Datum main_arg)
} }
else else
{ {
long delay_in_ms = 0; TimestampTz next_dump_time;
TimestampTz next_dump_time = 0; long delay_in_ms;
long secs = 0;
int usecs = 0;
/* Compute the next dump time. */ /* Compute the next dump time. */
next_dump_time = next_dump_time =
TimestampTzPlusMilliseconds(last_dump_time, TimestampTzPlusMilliseconds(last_dump_time,
autoprewarm_interval * 1000); autoprewarm_interval * 1000);
TimestampDifference(GetCurrentTimestamp(), next_dump_time, delay_in_ms =
&secs, &usecs); TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
delay_in_ms = secs + (usecs / 1000); next_dump_time);
/* Perform a dump if it's time. */ /* Perform a dump if it's time. */
if (delay_in_ms <= 0) if (delay_in_ms <= 0)
......
...@@ -1271,20 +1271,15 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime, PGresult **result) ...@@ -1271,20 +1271,15 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime, PGresult **result)
{ {
int wc; int wc;
TimestampTz now = GetCurrentTimestamp(); TimestampTz now = GetCurrentTimestamp();
long secs;
int microsecs;
long cur_timeout; long cur_timeout;
/* If timeout has expired, give up, else get sleep time. */ /* If timeout has expired, give up, else get sleep time. */
if (now >= endtime) cur_timeout = TimestampDifferenceMilliseconds(now, endtime);
if (cur_timeout <= 0)
{ {
timed_out = true; timed_out = true;
goto exit; goto exit;
} }
TimestampDifference(now, endtime, &secs, &microsecs);
/* To protect against clock skew, limit sleep to one minute. */
cur_timeout = Min(60000, secs * USECS_PER_SEC + microsecs);
/* Sleep until there's something to do */ /* Sleep until there's something to do */
wc = WaitLatchOrSocket(MyLatch, wc = WaitLatchOrSocket(MyLatch,
......
...@@ -6074,8 +6074,7 @@ recoveryApplyDelay(XLogReaderState *record) ...@@ -6074,8 +6074,7 @@ recoveryApplyDelay(XLogReaderState *record)
uint8 xact_info; uint8 xact_info;
TimestampTz xtime; TimestampTz xtime;
TimestampTz delayUntil; TimestampTz delayUntil;
long secs; long msecs;
int microsecs;
/* nothing to do if no delay configured */ /* nothing to do if no delay configured */
if (recovery_min_apply_delay <= 0) if (recovery_min_apply_delay <= 0)
...@@ -6115,8 +6114,8 @@ recoveryApplyDelay(XLogReaderState *record) ...@@ -6115,8 +6114,8 @@ recoveryApplyDelay(XLogReaderState *record)
* Exit without arming the latch if it's already past time to apply this * Exit without arming the latch if it's already past time to apply this
* record * record
*/ */
TimestampDifference(GetCurrentTimestamp(), delayUntil, &secs, &microsecs); msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(), delayUntil);
if (secs <= 0 && microsecs <= 0) if (msecs <= 0)
return false; return false;
while (true) while (true)
...@@ -6132,22 +6131,17 @@ recoveryApplyDelay(XLogReaderState *record) ...@@ -6132,22 +6131,17 @@ recoveryApplyDelay(XLogReaderState *record)
/* /*
* Wait for difference between GetCurrentTimestamp() and delayUntil * Wait for difference between GetCurrentTimestamp() and delayUntil
*/ */
TimestampDifference(GetCurrentTimestamp(), delayUntil, msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
&secs, &microsecs); delayUntil);
/* if (msecs <= 0)
* NB: We're ignoring waits below recovery_min_apply_delay's
* resolution.
*/
if (secs <= 0 && microsecs / 1000 <= 0)
break; break;
elog(DEBUG2, "recovery apply delay %ld seconds, %d milliseconds", elog(DEBUG2, "recovery apply delay %ld milliseconds", msecs);
secs, microsecs / 1000);
(void) WaitLatch(MyLatch, (void) WaitLatch(MyLatch,
WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
secs * 1000L + microsecs / 1000, msecs,
WAIT_EVENT_RECOVERY_APPLY_DELAY); WAIT_EVENT_RECOVERY_APPLY_DELAY);
} }
return true; return true;
...@@ -8555,33 +8549,24 @@ LogCheckpointStart(int flags, bool restartpoint) ...@@ -8555,33 +8549,24 @@ LogCheckpointStart(int flags, bool restartpoint)
static void static void
LogCheckpointEnd(bool restartpoint) LogCheckpointEnd(bool restartpoint)
{ {
long write_secs, long write_msecs,
sync_secs, sync_msecs,
total_secs, total_msecs,
longest_secs, longest_msecs,
average_secs; average_msecs;
int write_usecs,
sync_usecs,
total_usecs,
longest_usecs,
average_usecs;
uint64 average_sync_time; uint64 average_sync_time;
CheckpointStats.ckpt_end_t = GetCurrentTimestamp(); CheckpointStats.ckpt_end_t = GetCurrentTimestamp();
TimestampDifference(CheckpointStats.ckpt_write_t, write_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_write_t,
CheckpointStats.ckpt_sync_t, CheckpointStats.ckpt_sync_t);
&write_secs, &write_usecs);
TimestampDifference(CheckpointStats.ckpt_sync_t, sync_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_sync_t,
CheckpointStats.ckpt_sync_end_t, CheckpointStats.ckpt_sync_end_t);
&sync_secs, &sync_usecs);
/* Accumulate checkpoint timing summary data, in milliseconds. */ /* Accumulate checkpoint timing summary data, in milliseconds. */
BgWriterStats.m_checkpoint_write_time += BgWriterStats.m_checkpoint_write_time += write_msecs;
write_secs * 1000 + write_usecs / 1000; BgWriterStats.m_checkpoint_sync_time += sync_msecs;
BgWriterStats.m_checkpoint_sync_time +=
sync_secs * 1000 + sync_usecs / 1000;
/* /*
* All of the published timing statistics are accounted for. Only * All of the published timing statistics are accounted for. Only
...@@ -8590,25 +8575,20 @@ LogCheckpointEnd(bool restartpoint) ...@@ -8590,25 +8575,20 @@ LogCheckpointEnd(bool restartpoint)
if (!log_checkpoints) if (!log_checkpoints)
return; return;
TimestampDifference(CheckpointStats.ckpt_start_t, total_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_start_t,
CheckpointStats.ckpt_end_t, CheckpointStats.ckpt_end_t);
&total_secs, &total_usecs);
/* /*
* Timing values returned from CheckpointStats are in microseconds. * Timing values returned from CheckpointStats are in microseconds.
* Convert to the second plus microsecond form that TimestampDifference * Convert to milliseconds for consistent printing.
* returns for homogeneous printing.
*/ */
longest_secs = (long) (CheckpointStats.ckpt_longest_sync / 1000000); longest_msecs = (long) ((CheckpointStats.ckpt_longest_sync + 999) / 1000);
longest_usecs = CheckpointStats.ckpt_longest_sync -
(uint64) longest_secs * 1000000;
average_sync_time = 0; average_sync_time = 0;
if (CheckpointStats.ckpt_sync_rels > 0) if (CheckpointStats.ckpt_sync_rels > 0)
average_sync_time = CheckpointStats.ckpt_agg_sync_time / average_sync_time = CheckpointStats.ckpt_agg_sync_time /
CheckpointStats.ckpt_sync_rels; CheckpointStats.ckpt_sync_rels;
average_secs = (long) (average_sync_time / 1000000); average_msecs = (long) ((average_sync_time + 999) / 1000);
average_usecs = average_sync_time - (uint64) average_secs * 1000000;
elog(LOG, "%s complete: wrote %d buffers (%.1f%%); " elog(LOG, "%s complete: wrote %d buffers (%.1f%%); "
"%d WAL file(s) added, %d removed, %d recycled; " "%d WAL file(s) added, %d removed, %d recycled; "
...@@ -8621,12 +8601,12 @@ LogCheckpointEnd(bool restartpoint) ...@@ -8621,12 +8601,12 @@ LogCheckpointEnd(bool restartpoint)
CheckpointStats.ckpt_segs_added, CheckpointStats.ckpt_segs_added,
CheckpointStats.ckpt_segs_removed, CheckpointStats.ckpt_segs_removed,
CheckpointStats.ckpt_segs_recycled, CheckpointStats.ckpt_segs_recycled,
write_secs, write_usecs / 1000, write_msecs / 1000, (int) (write_msecs % 1000),
sync_secs, sync_usecs / 1000, sync_msecs / 1000, (int) (sync_msecs % 1000),
total_secs, total_usecs / 1000, total_msecs / 1000, (int) (total_msecs % 1000),
CheckpointStats.ckpt_sync_rels, CheckpointStats.ckpt_sync_rels,
longest_secs, longest_usecs / 1000, longest_msecs / 1000, (int) (longest_msecs % 1000),
average_secs, average_usecs / 1000, average_msecs / 1000, (int) (average_msecs % 1000),
(int) (PrevCheckPointDistance / 1024.0), (int) (PrevCheckPointDistance / 1024.0),
(int) (CheckPointDistanceEstimate / 1024.0)); (int) (CheckPointDistanceEstimate / 1024.0));
} }
...@@ -12233,13 +12213,10 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, ...@@ -12233,13 +12213,10 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
if (!TimestampDifferenceExceeds(last_fail_time, now, if (!TimestampDifferenceExceeds(last_fail_time, now,
wal_retrieve_retry_interval)) wal_retrieve_retry_interval))
{ {
long secs, long wait_time;
wait_time;
int usecs;
TimestampDifference(last_fail_time, now, &secs, &usecs);
wait_time = wal_retrieve_retry_interval - wait_time = wal_retrieve_retry_interval -
(secs * 1000 + usecs / 1000); TimestampDifferenceMilliseconds(last_fail_time, now);
(void) WaitLatch(MyLatch, (void) WaitLatch(MyLatch,
WL_LATCH_SET | WL_TIMEOUT | WL_LATCH_SET | WL_TIMEOUT |
......
...@@ -350,10 +350,6 @@ GetReplicationApplyDelay(void) ...@@ -350,10 +350,6 @@ GetReplicationApplyDelay(void)
WalRcvData *walrcv = WalRcv; WalRcvData *walrcv = WalRcv;
XLogRecPtr receivePtr; XLogRecPtr receivePtr;
XLogRecPtr replayPtr; XLogRecPtr replayPtr;
long secs;
int usecs;
TimestampTz chunkReplayStartTime; TimestampTz chunkReplayStartTime;
SpinLockAcquire(&walrcv->mutex); SpinLockAcquire(&walrcv->mutex);
...@@ -370,11 +366,8 @@ GetReplicationApplyDelay(void) ...@@ -370,11 +366,8 @@ GetReplicationApplyDelay(void)
if (chunkReplayStartTime == 0) if (chunkReplayStartTime == 0)
return -1; return -1;
TimestampDifference(chunkReplayStartTime, return TimestampDifferenceMilliseconds(chunkReplayStartTime,
GetCurrentTimestamp(), GetCurrentTimestamp());
&secs, &usecs);
return (((int) secs * 1000) + (usecs / 1000));
} }
/* /*
...@@ -385,24 +378,14 @@ int ...@@ -385,24 +378,14 @@ int
GetReplicationTransferLatency(void) GetReplicationTransferLatency(void)
{ {
WalRcvData *walrcv = WalRcv; WalRcvData *walrcv = WalRcv;
TimestampTz lastMsgSendTime; TimestampTz lastMsgSendTime;
TimestampTz lastMsgReceiptTime; TimestampTz lastMsgReceiptTime;
long secs = 0;
int usecs = 0;
int ms;
SpinLockAcquire(&walrcv->mutex); SpinLockAcquire(&walrcv->mutex);
lastMsgSendTime = walrcv->lastMsgSendTime; lastMsgSendTime = walrcv->lastMsgSendTime;
lastMsgReceiptTime = walrcv->lastMsgReceiptTime; lastMsgReceiptTime = walrcv->lastMsgReceiptTime;
SpinLockRelease(&walrcv->mutex); SpinLockRelease(&walrcv->mutex);
TimestampDifference(lastMsgSendTime, return TimestampDifferenceMilliseconds(lastMsgSendTime,
lastMsgReceiptTime, lastMsgReceiptTime);
&secs, &usecs);
ms = ((int) secs * 1000) + (usecs / 1000);
return ms;
} }
...@@ -2194,8 +2194,6 @@ WalSndComputeSleeptime(TimestampTz now) ...@@ -2194,8 +2194,6 @@ WalSndComputeSleeptime(TimestampTz now)
if (wal_sender_timeout > 0 && last_reply_timestamp > 0) if (wal_sender_timeout > 0 && last_reply_timestamp > 0)
{ {
TimestampTz wakeup_time; TimestampTz wakeup_time;
long sec_to_timeout;
int microsec_to_timeout;
/* /*
* At the latest stop sleeping once wal_sender_timeout has been * At the latest stop sleeping once wal_sender_timeout has been
...@@ -2214,11 +2212,7 @@ WalSndComputeSleeptime(TimestampTz now) ...@@ -2214,11 +2212,7 @@ WalSndComputeSleeptime(TimestampTz now)
wal_sender_timeout / 2); wal_sender_timeout / 2);
/* Compute relative time until wakeup. */ /* Compute relative time until wakeup. */
TimestampDifference(now, wakeup_time, sleeptime = TimestampDifferenceMilliseconds(now, wakeup_time);
&sec_to_timeout, &microsec_to_timeout);
sleeptime = sec_to_timeout * 1000 +
microsec_to_timeout / 1000;
} }
return sleeptime; return sleeptime;
......
...@@ -1641,12 +1641,14 @@ timeofday(PG_FUNCTION_ARGS) ...@@ -1641,12 +1641,14 @@ timeofday(PG_FUNCTION_ARGS)
* TimestampDifference -- convert the difference between two timestamps * TimestampDifference -- convert the difference between two timestamps
* into integer seconds and microseconds * into integer seconds and microseconds
* *
* This is typically used to calculate a wait timeout for select(2),
* which explains the otherwise-odd choice of output format.
*
* Both inputs must be ordinary finite timestamps (in current usage, * Both inputs must be ordinary finite timestamps (in current usage,
* they'll be results from GetCurrentTimestamp()). * they'll be results from GetCurrentTimestamp()).
* *
* We expect start_time <= stop_time. If not, we return zeros; for current * We expect start_time <= stop_time. If not, we return zeros,
* callers there is no need to be tense about which way division rounds on * since then we're already past the previously determined stop_time.
* negative inputs.
*/ */
void void
TimestampDifference(TimestampTz start_time, TimestampTz stop_time, TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
...@@ -1666,6 +1668,36 @@ TimestampDifference(TimestampTz start_time, TimestampTz stop_time, ...@@ -1666,6 +1668,36 @@ TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
} }
} }
/*
* TimestampDifferenceMilliseconds -- convert the difference between two
* timestamps into integer milliseconds
*
* This is typically used to calculate a wait timeout for WaitLatch()
* or a related function. The choice of "long" as the result type
* is to harmonize with that. It is caller's responsibility that the
* input timestamps not be so far apart as to risk overflow of "long"
* (which'd happen at about 25 days on machines with 32-bit "long").
*
* Both inputs must be ordinary finite timestamps (in current usage,
* they'll be results from GetCurrentTimestamp()).
*
* We expect start_time <= stop_time. If not, we return zero,
* since then we're already past the previously determined stop_time.
*
* Note we round up any fractional millisecond, since waiting for just
* less than the intended timeout is undesirable.
*/
long
TimestampDifferenceMilliseconds(TimestampTz start_time, TimestampTz stop_time)
{
TimestampTz diff = stop_time - start_time;
if (diff <= 0)
return 0;
else
return (long) ((diff + 999) / 1000);
}
/* /*
* TimestampDifferenceExceeds -- report whether the difference between two * TimestampDifferenceExceeds -- report whether the difference between two
* timestamps is >= a threshold (expressed in milliseconds) * timestamps is >= a threshold (expressed in milliseconds)
......
...@@ -72,6 +72,8 @@ extern TimestampTz GetSQLCurrentTimestamp(int32 typmod); ...@@ -72,6 +72,8 @@ extern TimestampTz GetSQLCurrentTimestamp(int32 typmod);
extern Timestamp GetSQLLocalTimestamp(int32 typmod); extern Timestamp GetSQLLocalTimestamp(int32 typmod);
extern void TimestampDifference(TimestampTz start_time, TimestampTz stop_time, extern void TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
long *secs, int *microsecs); long *secs, int *microsecs);
extern long TimestampDifferenceMilliseconds(TimestampTz start_time,
TimestampTz stop_time);
extern bool TimestampDifferenceExceeds(TimestampTz start_time, extern bool TimestampDifferenceExceeds(TimestampTz start_time,
TimestampTz stop_time, TimestampTz stop_time,
int msec); int msec);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment