mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-23 19:39:53 +08:00
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
b8b6a0124b
commit
ec29427ce2
@ -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, µ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 */
|
||||
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, µsecs);
|
||||
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, µsecs);
|
||||
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, µsec_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);
|
||||
|
Loading…
Reference in New Issue
Block a user