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)
}
else
{
long delay_in_ms = 0;
TimestampTz next_dump_time = 0;
long secs = 0;
int usecs = 0;
TimestampTz next_dump_time;
long delay_in_ms;
/* Compute the next dump time. */
next_dump_time =
TimestampTzPlusMilliseconds(last_dump_time,
autoprewarm_interval * 1000);
TimestampDifference(GetCurrentTimestamp(), next_dump_time,
&secs, &usecs);
delay_in_ms = secs + (usecs / 1000);
delay_in_ms =
TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
next_dump_time);
/* Perform a dump if it's time. */
if (delay_in_ms <= 0)
......
......@@ -1271,20 +1271,15 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime, PGresult **result)
{
int wc;
TimestampTz now = GetCurrentTimestamp();
long secs;
int microsecs;
long cur_timeout;
/* 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;
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 */
wc = WaitLatchOrSocket(MyLatch,
......
......@@ -6074,8 +6074,7 @@ recoveryApplyDelay(XLogReaderState *record)
uint8 xact_info;
TimestampTz xtime;
TimestampTz delayUntil;
long secs;
int microsecs;
long msecs;
/* nothing to do if no delay configured */
if (recovery_min_apply_delay <= 0)
......@@ -6115,8 +6114,8 @@ recoveryApplyDelay(XLogReaderState *record)
* Exit without arming the latch if it's already past time to apply this
* record
*/
TimestampDifference(GetCurrentTimestamp(), delayUntil, &secs, &microsecs);
if (secs <= 0 && microsecs <= 0)
msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(), delayUntil);
if (msecs <= 0)
return false;
while (true)
......@@ -6132,22 +6131,17 @@ recoveryApplyDelay(XLogReaderState *record)
/*
* Wait for difference between GetCurrentTimestamp() and delayUntil
*/
TimestampDifference(GetCurrentTimestamp(), delayUntil,
&secs, &microsecs);
msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
delayUntil);
/*
* NB: We're ignoring waits below recovery_min_apply_delay's
* resolution.
*/
if (secs <= 0 && microsecs / 1000 <= 0)
if (msecs <= 0)
break;
elog(DEBUG2, "recovery apply delay %ld seconds, %d milliseconds",
secs, microsecs / 1000);
elog(DEBUG2, "recovery apply delay %ld milliseconds", msecs);
(void) WaitLatch(MyLatch,
WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
secs * 1000L + microsecs / 1000,
msecs,
WAIT_EVENT_RECOVERY_APPLY_DELAY);
}
return true;
......@@ -8555,33 +8549,24 @@ LogCheckpointStart(int flags, bool restartpoint)
static void
LogCheckpointEnd(bool restartpoint)
{
long write_secs,
sync_secs,
total_secs,
longest_secs,
average_secs;
int write_usecs,
sync_usecs,
total_usecs,
longest_usecs,
average_usecs;
long write_msecs,
sync_msecs,
total_msecs,
longest_msecs,
average_msecs;
uint64 average_sync_time;
CheckpointStats.ckpt_end_t = GetCurrentTimestamp();
TimestampDifference(CheckpointStats.ckpt_write_t,
CheckpointStats.ckpt_sync_t,
&write_secs, &write_usecs);
write_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_write_t,
CheckpointStats.ckpt_sync_t);
TimestampDifference(CheckpointStats.ckpt_sync_t,
CheckpointStats.ckpt_sync_end_t,
&sync_secs, &sync_usecs);
sync_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_sync_t,
CheckpointStats.ckpt_sync_end_t);
/* Accumulate checkpoint timing summary data, in milliseconds. */
BgWriterStats.m_checkpoint_write_time +=
write_secs * 1000 + write_usecs / 1000;
BgWriterStats.m_checkpoint_sync_time +=
sync_secs * 1000 + sync_usecs / 1000;
BgWriterStats.m_checkpoint_write_time += write_msecs;
BgWriterStats.m_checkpoint_sync_time += sync_msecs;
/*
* All of the published timing statistics are accounted for. Only
......@@ -8590,25 +8575,20 @@ LogCheckpointEnd(bool restartpoint)
if (!log_checkpoints)
return;
TimestampDifference(CheckpointStats.ckpt_start_t,
CheckpointStats.ckpt_end_t,
&total_secs, &total_usecs);
total_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_start_t,
CheckpointStats.ckpt_end_t);
/*
* Timing values returned from CheckpointStats are in microseconds.
* Convert to the second plus microsecond form that TimestampDifference
* returns for homogeneous printing.
* Convert to milliseconds for consistent printing.
*/
longest_secs = (long) (CheckpointStats.ckpt_longest_sync / 1000000);
longest_usecs = CheckpointStats.ckpt_longest_sync -
(uint64) longest_secs * 1000000;
longest_msecs = (long) ((CheckpointStats.ckpt_longest_sync + 999) / 1000);
average_sync_time = 0;
if (CheckpointStats.ckpt_sync_rels > 0)
average_sync_time = CheckpointStats.ckpt_agg_sync_time /
CheckpointStats.ckpt_sync_rels;
average_secs = (long) (average_sync_time / 1000000);
average_usecs = average_sync_time - (uint64) average_secs * 1000000;
average_msecs = (long) ((average_sync_time + 999) / 1000);
elog(LOG, "%s complete: wrote %d buffers (%.1f%%); "
"%d WAL file(s) added, %d removed, %d recycled; "
......@@ -8621,12 +8601,12 @@ LogCheckpointEnd(bool restartpoint)
CheckpointStats.ckpt_segs_added,
CheckpointStats.ckpt_segs_removed,
CheckpointStats.ckpt_segs_recycled,
write_secs, write_usecs / 1000,
sync_secs, sync_usecs / 1000,
total_secs, total_usecs / 1000,
write_msecs / 1000, (int) (write_msecs % 1000),
sync_msecs / 1000, (int) (sync_msecs % 1000),
total_msecs / 1000, (int) (total_msecs % 1000),
CheckpointStats.ckpt_sync_rels,
longest_secs, longest_usecs / 1000,
average_secs, average_usecs / 1000,
longest_msecs / 1000, (int) (longest_msecs % 1000),
average_msecs / 1000, (int) (average_msecs % 1000),
(int) (PrevCheckPointDistance / 1024.0),
(int) (CheckPointDistanceEstimate / 1024.0));
}
......@@ -12233,13 +12213,10 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
if (!TimestampDifferenceExceeds(last_fail_time, now,
wal_retrieve_retry_interval))
{
long secs,
wait_time;
int usecs;
long wait_time;
TimestampDifference(last_fail_time, now, &secs, &usecs);
wait_time = wal_retrieve_retry_interval -
(secs * 1000 + usecs / 1000);
TimestampDifferenceMilliseconds(last_fail_time, now);
(void) WaitLatch(MyLatch,
WL_LATCH_SET | WL_TIMEOUT |
......
......@@ -350,10 +350,6 @@ GetReplicationApplyDelay(void)
WalRcvData *walrcv = WalRcv;
XLogRecPtr receivePtr;
XLogRecPtr replayPtr;
long secs;
int usecs;
TimestampTz chunkReplayStartTime;
SpinLockAcquire(&walrcv->mutex);
......@@ -370,11 +366,8 @@ GetReplicationApplyDelay(void)
if (chunkReplayStartTime == 0)
return -1;
TimestampDifference(chunkReplayStartTime,
GetCurrentTimestamp(),
&secs, &usecs);
return (((int) secs * 1000) + (usecs / 1000));
return TimestampDifferenceMilliseconds(chunkReplayStartTime,
GetCurrentTimestamp());
}
/*
......@@ -385,24 +378,14 @@ int
GetReplicationTransferLatency(void)
{
WalRcvData *walrcv = WalRcv;
TimestampTz lastMsgSendTime;
TimestampTz lastMsgReceiptTime;
long secs = 0;
int usecs = 0;
int ms;
SpinLockAcquire(&walrcv->mutex);
lastMsgSendTime = walrcv->lastMsgSendTime;
lastMsgReceiptTime = walrcv->lastMsgReceiptTime;
SpinLockRelease(&walrcv->mutex);
TimestampDifference(lastMsgSendTime,
lastMsgReceiptTime,
&secs, &usecs);
ms = ((int) secs * 1000) + (usecs / 1000);
return ms;
return TimestampDifferenceMilliseconds(lastMsgSendTime,
lastMsgReceiptTime);
}
......@@ -2194,8 +2194,6 @@ WalSndComputeSleeptime(TimestampTz now)
if (wal_sender_timeout > 0 && last_reply_timestamp > 0)
{
TimestampTz wakeup_time;
long sec_to_timeout;
int microsec_to_timeout;
/*
* At the latest stop sleeping once wal_sender_timeout has been
......@@ -2214,11 +2212,7 @@ WalSndComputeSleeptime(TimestampTz now)
wal_sender_timeout / 2);
/* Compute relative time until wakeup. */
TimestampDifference(now, wakeup_time,
&sec_to_timeout, &microsec_to_timeout);
sleeptime = sec_to_timeout * 1000 +
microsec_to_timeout / 1000;
sleeptime = TimestampDifferenceMilliseconds(now, wakeup_time);
}
return sleeptime;
......
......@@ -1641,12 +1641,14 @@ timeofday(PG_FUNCTION_ARGS)
* TimestampDifference -- convert the difference between two timestamps
* 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,
* they'll be results from GetCurrentTimestamp()).
*
* We expect start_time <= stop_time. If not, we return zeros; for current
* callers there is no need to be tense about which way division rounds on
* negative inputs.
* We expect start_time <= stop_time. If not, we return zeros,
* since then we're already past the previously determined stop_time.
*/
void
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
* timestamps is >= a threshold (expressed in milliseconds)
......
......@@ -72,6 +72,8 @@ extern TimestampTz GetSQLCurrentTimestamp(int32 typmod);
extern Timestamp GetSQLLocalTimestamp(int32 typmod);
extern void TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
long *secs, int *microsecs);
extern long TimestampDifferenceMilliseconds(TimestampTz start_time,
TimestampTz stop_time);
extern bool TimestampDifferenceExceeds(TimestampTz start_time,
TimestampTz stop_time,
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