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
This commit is contained in:
parent
9fed2b5b2e
commit
3a89ea0eb6
@ -226,18 +226,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)
|
||||||
|
@ -1139,20 +1139,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, µsecs);
|
|
||||||
|
|
||||||
/* 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,
|
||||||
|
@ -6128,8 +6128,7 @@ recoveryApplyDelay(XLogReaderState *record)
|
|||||||
{
|
{
|
||||||
uint8 xact_info;
|
uint8 xact_info;
|
||||||
TimestampTz xtime;
|
TimestampTz xtime;
|
||||||
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)
|
||||||
@ -6166,9 +6165,9 @@ 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(), recoveryDelayUntilTime,
|
msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
|
||||||
&secs, µsecs);
|
recoveryDelayUntilTime);
|
||||||
if (secs <= 0 && microsecs <= 0)
|
if (msecs <= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
@ -6185,19 +6184,17 @@ recoveryApplyDelay(XLogReaderState *record)
|
|||||||
* Wait for difference between GetCurrentTimestamp() and
|
* Wait for difference between GetCurrentTimestamp() and
|
||||||
* recoveryDelayUntilTime
|
* recoveryDelayUntilTime
|
||||||
*/
|
*/
|
||||||
TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
|
msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
|
||||||
&secs, µsecs);
|
recoveryDelayUntilTime);
|
||||||
|
|
||||||
/* NB: We're ignoring waits below min_apply_delay's resolution. */
|
if (msecs <= 0)
|
||||||
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);
|
|
||||||
|
|
||||||
WaitLatch(&XLogCtl->recoveryWakeupLatch,
|
(void) WaitLatch(&XLogCtl->recoveryWakeupLatch,
|
||||||
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
|
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
|
||||||
secs * 1000L + microsecs / 1000,
|
msecs,
|
||||||
WAIT_EVENT_RECOVERY_APPLY_DELAY);
|
WAIT_EVENT_RECOVERY_APPLY_DELAY);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -8585,33 +8582,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
|
||||||
@ -8620,25 +8608,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; "
|
||||||
@ -8651,12 +8634,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));
|
||||||
}
|
}
|
||||||
@ -12168,13 +12151,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);
|
||||||
|
|
||||||
WaitLatch(&XLogCtl->recoveryWakeupLatch,
|
WaitLatch(&XLogCtl->recoveryWakeupLatch,
|
||||||
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
|
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
|
||||||
|
@ -320,10 +320,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);
|
||||||
@ -340,11 +336,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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -355,24 +348,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;
|
|
||||||
}
|
}
|
||||||
|
@ -2033,8 +2033,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
|
||||||
@ -2053,11 +2051,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, µsec_to_timeout);
|
|
||||||
|
|
||||||
sleeptime = sec_to_timeout * 1000 +
|
|
||||||
microsec_to_timeout / 1000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sleeptime;
|
return sleeptime;
|
||||||
|
@ -1604,12 +1604,14 @@ GetSQLLocalTimestamp(int32 typmod)
|
|||||||
* 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,
|
||||||
@ -1629,6 +1631,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);
|
|||||||
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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user