Skip to content

Commit 3a89ea0

Browse files
committed
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
1 parent 9fed2b5 commit 3a89ea0

File tree

7 files changed

+84
-100
lines changed

7 files changed

+84
-100
lines changed

contrib/pg_prewarm/autoprewarm.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -226,18 +226,16 @@ autoprewarm_main(Datum main_arg)
226226
}
227227
else
228228
{
229-
long delay_in_ms = 0;
230-
TimestampTz next_dump_time = 0;
231-
long secs = 0;
232-
int usecs = 0;
229+
TimestampTz next_dump_time;
230+
long delay_in_ms;
233231

234232
/* Compute the next dump time. */
235233
next_dump_time =
236234
TimestampTzPlusMilliseconds(last_dump_time,
237235
autoprewarm_interval * 1000);
238-
TimestampDifference(GetCurrentTimestamp(), next_dump_time,
239-
&secs, &usecs);
240-
delay_in_ms = secs + (usecs / 1000);
236+
delay_in_ms =
237+
TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
238+
next_dump_time);
241239

242240
/* Perform a dump if it's time. */
243241
if (delay_in_ms <= 0)

contrib/postgres_fdw/connection.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,20 +1139,15 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime, PGresult **result)
11391139
{
11401140
int wc;
11411141
TimestampTz now = GetCurrentTimestamp();
1142-
long secs;
1143-
int microsecs;
11441142
long cur_timeout;
11451143

11461144
/* If timeout has expired, give up, else get sleep time. */
1147-
if (now >= endtime)
1145+
cur_timeout = TimestampDifferenceMilliseconds(now, endtime);
1146+
if (cur_timeout <= 0)
11481147
{
11491148
timed_out = true;
11501149
goto exit;
11511150
}
1152-
TimestampDifference(now, endtime, &secs, &microsecs);
1153-
1154-
/* To protect against clock skew, limit sleep to one minute. */
1155-
cur_timeout = Min(60000, secs * USECS_PER_SEC + microsecs);
11561151

11571152
/* Sleep until there's something to do */
11581153
wc = WaitLatchOrSocket(MyLatch,

src/backend/access/transam/xlog.c

Lines changed: 35 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6128,8 +6128,7 @@ recoveryApplyDelay(XLogReaderState *record)
61286128
{
61296129
uint8 xact_info;
61306130
TimestampTz xtime;
6131-
long secs;
6132-
int microsecs;
6131+
long msecs;
61336132

61346133
/* nothing to do if no delay configured */
61356134
if (recovery_min_apply_delay <= 0)
@@ -6166,9 +6165,9 @@ recoveryApplyDelay(XLogReaderState *record)
61666165
* Exit without arming the latch if it's already past time to apply this
61676166
* record
61686167
*/
6169-
TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
6170-
&secs, &microsecs);
6171-
if (secs <= 0 && microsecs <= 0)
6168+
msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
6169+
recoveryDelayUntilTime);
6170+
if (msecs <= 0)
61726171
return false;
61736172

61746173
while (true)
@@ -6185,20 +6184,18 @@ recoveryApplyDelay(XLogReaderState *record)
61856184
* Wait for difference between GetCurrentTimestamp() and
61866185
* recoveryDelayUntilTime
61876186
*/
6188-
TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
6189-
&secs, &microsecs);
6187+
msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
6188+
recoveryDelayUntilTime);
61906189

6191-
/* NB: We're ignoring waits below min_apply_delay's resolution. */
6192-
if (secs <= 0 && microsecs / 1000 <= 0)
6190+
if (msecs <= 0)
61936191
break;
61946192

6195-
elog(DEBUG2, "recovery apply delay %ld seconds, %d milliseconds",
6196-
secs, microsecs / 1000);
6193+
elog(DEBUG2, "recovery apply delay %ld milliseconds", msecs);
61976194

6198-
WaitLatch(&XLogCtl->recoveryWakeupLatch,
6199-
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
6200-
secs * 1000L + microsecs / 1000,
6201-
WAIT_EVENT_RECOVERY_APPLY_DELAY);
6195+
(void) WaitLatch(&XLogCtl->recoveryWakeupLatch,
6196+
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
6197+
msecs,
6198+
WAIT_EVENT_RECOVERY_APPLY_DELAY);
62026199
}
62036200
return true;
62046201
}
@@ -8585,33 +8582,24 @@ LogCheckpointStart(int flags, bool restartpoint)
85858582
static void
85868583
LogCheckpointEnd(bool restartpoint)
85878584
{
8588-
long write_secs,
8589-
sync_secs,
8590-
total_secs,
8591-
longest_secs,
8592-
average_secs;
8593-
int write_usecs,
8594-
sync_usecs,
8595-
total_usecs,
8596-
longest_usecs,
8597-
average_usecs;
8585+
long write_msecs,
8586+
sync_msecs,
8587+
total_msecs,
8588+
longest_msecs,
8589+
average_msecs;
85988590
uint64 average_sync_time;
85998591

86008592
CheckpointStats.ckpt_end_t = GetCurrentTimestamp();
86018593

8602-
TimestampDifference(CheckpointStats.ckpt_write_t,
8603-
CheckpointStats.ckpt_sync_t,
8604-
&write_secs, &write_usecs);
8594+
write_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_write_t,
8595+
CheckpointStats.ckpt_sync_t);
86058596

8606-
TimestampDifference(CheckpointStats.ckpt_sync_t,
8607-
CheckpointStats.ckpt_sync_end_t,
8608-
&sync_secs, &sync_usecs);
8597+
sync_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_sync_t,
8598+
CheckpointStats.ckpt_sync_end_t);
86098599

86108600
/* Accumulate checkpoint timing summary data, in milliseconds. */
8611-
BgWriterStats.m_checkpoint_write_time +=
8612-
write_secs * 1000 + write_usecs / 1000;
8613-
BgWriterStats.m_checkpoint_sync_time +=
8614-
sync_secs * 1000 + sync_usecs / 1000;
8601+
BgWriterStats.m_checkpoint_write_time += write_msecs;
8602+
BgWriterStats.m_checkpoint_sync_time += sync_msecs;
86158603

86168604
/*
86178605
* All of the published timing statistics are accounted for. Only
@@ -8620,25 +8608,20 @@ LogCheckpointEnd(bool restartpoint)
86208608
if (!log_checkpoints)
86218609
return;
86228610

8623-
TimestampDifference(CheckpointStats.ckpt_start_t,
8624-
CheckpointStats.ckpt_end_t,
8625-
&total_secs, &total_usecs);
8611+
total_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_start_t,
8612+
CheckpointStats.ckpt_end_t);
86268613

86278614
/*
86288615
* Timing values returned from CheckpointStats are in microseconds.
8629-
* Convert to the second plus microsecond form that TimestampDifference
8630-
* returns for homogeneous printing.
8616+
* Convert to milliseconds for consistent printing.
86318617
*/
8632-
longest_secs = (long) (CheckpointStats.ckpt_longest_sync / 1000000);
8633-
longest_usecs = CheckpointStats.ckpt_longest_sync -
8634-
(uint64) longest_secs * 1000000;
8618+
longest_msecs = (long) ((CheckpointStats.ckpt_longest_sync + 999) / 1000);
86358619

86368620
average_sync_time = 0;
86378621
if (CheckpointStats.ckpt_sync_rels > 0)
86388622
average_sync_time = CheckpointStats.ckpt_agg_sync_time /
86398623
CheckpointStats.ckpt_sync_rels;
8640-
average_secs = (long) (average_sync_time / 1000000);
8641-
average_usecs = average_sync_time - (uint64) average_secs * 1000000;
8624+
average_msecs = (long) ((average_sync_time + 999) / 1000);
86428625

86438626
elog(LOG, "%s complete: wrote %d buffers (%.1f%%); "
86448627
"%d WAL file(s) added, %d removed, %d recycled; "
@@ -8651,12 +8634,12 @@ LogCheckpointEnd(bool restartpoint)
86518634
CheckpointStats.ckpt_segs_added,
86528635
CheckpointStats.ckpt_segs_removed,
86538636
CheckpointStats.ckpt_segs_recycled,
8654-
write_secs, write_usecs / 1000,
8655-
sync_secs, sync_usecs / 1000,
8656-
total_secs, total_usecs / 1000,
8637+
write_msecs / 1000, (int) (write_msecs % 1000),
8638+
sync_msecs / 1000, (int) (sync_msecs % 1000),
8639+
total_msecs / 1000, (int) (total_msecs % 1000),
86578640
CheckpointStats.ckpt_sync_rels,
8658-
longest_secs, longest_usecs / 1000,
8659-
average_secs, average_usecs / 1000,
8641+
longest_msecs / 1000, (int) (longest_msecs % 1000),
8642+
average_msecs / 1000, (int) (average_msecs % 1000),
86608643
(int) (PrevCheckPointDistance / 1024.0),
86618644
(int) (CheckPointDistanceEstimate / 1024.0));
86628645
}
@@ -12168,13 +12151,10 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
1216812151
if (!TimestampDifferenceExceeds(last_fail_time, now,
1216912152
wal_retrieve_retry_interval))
1217012153
{
12171-
long secs,
12172-
wait_time;
12173-
int usecs;
12154+
long wait_time;
1217412155

12175-
TimestampDifference(last_fail_time, now, &secs, &usecs);
1217612156
wait_time = wal_retrieve_retry_interval -
12177-
(secs * 1000 + usecs / 1000);
12157+
TimestampDifferenceMilliseconds(last_fail_time, now);
1217812158

1217912159
WaitLatch(&XLogCtl->recoveryWakeupLatch,
1218012160
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,

src/backend/replication/walreceiverfuncs.c

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -320,10 +320,6 @@ GetReplicationApplyDelay(void)
320320
WalRcvData *walrcv = WalRcv;
321321
XLogRecPtr receivePtr;
322322
XLogRecPtr replayPtr;
323-
324-
long secs;
325-
int usecs;
326-
327323
TimestampTz chunkReplayStartTime;
328324

329325
SpinLockAcquire(&walrcv->mutex);
@@ -340,11 +336,8 @@ GetReplicationApplyDelay(void)
340336
if (chunkReplayStartTime == 0)
341337
return -1;
342338

343-
TimestampDifference(chunkReplayStartTime,
344-
GetCurrentTimestamp(),
345-
&secs, &usecs);
346-
347-
return (((int) secs * 1000) + (usecs / 1000));
339+
return TimestampDifferenceMilliseconds(chunkReplayStartTime,
340+
GetCurrentTimestamp());
348341
}
349342

350343
/*
@@ -355,24 +348,14 @@ int
355348
GetReplicationTransferLatency(void)
356349
{
357350
WalRcvData *walrcv = WalRcv;
358-
359351
TimestampTz lastMsgSendTime;
360352
TimestampTz lastMsgReceiptTime;
361353

362-
long secs = 0;
363-
int usecs = 0;
364-
int ms;
365-
366354
SpinLockAcquire(&walrcv->mutex);
367355
lastMsgSendTime = walrcv->lastMsgSendTime;
368356
lastMsgReceiptTime = walrcv->lastMsgReceiptTime;
369357
SpinLockRelease(&walrcv->mutex);
370358

371-
TimestampDifference(lastMsgSendTime,
372-
lastMsgReceiptTime,
373-
&secs, &usecs);
374-
375-
ms = ((int) secs * 1000) + (usecs / 1000);
376-
377-
return ms;
359+
return TimestampDifferenceMilliseconds(lastMsgSendTime,
360+
lastMsgReceiptTime);
378361
}

src/backend/replication/walsender.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,8 +2033,6 @@ WalSndComputeSleeptime(TimestampTz now)
20332033
if (wal_sender_timeout > 0 && last_reply_timestamp > 0)
20342034
{
20352035
TimestampTz wakeup_time;
2036-
long sec_to_timeout;
2037-
int microsec_to_timeout;
20382036

20392037
/*
20402038
* At the latest stop sleeping once wal_sender_timeout has been
@@ -2053,11 +2051,7 @@ WalSndComputeSleeptime(TimestampTz now)
20532051
wal_sender_timeout / 2);
20542052

20552053
/* Compute relative time until wakeup. */
2056-
TimestampDifference(now, wakeup_time,
2057-
&sec_to_timeout, &microsec_to_timeout);
2058-
2059-
sleeptime = sec_to_timeout * 1000 +
2060-
microsec_to_timeout / 1000;
2054+
sleeptime = TimestampDifferenceMilliseconds(now, wakeup_time);
20612055
}
20622056

20632057
return sleeptime;

src/backend/utils/adt/timestamp.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,12 +1604,14 @@ GetSQLLocalTimestamp(int32 typmod)
16041604
* TimestampDifference -- convert the difference between two timestamps
16051605
* into integer seconds and microseconds
16061606
*
1607+
* This is typically used to calculate a wait timeout for select(2),
1608+
* which explains the otherwise-odd choice of output format.
1609+
*
16071610
* Both inputs must be ordinary finite timestamps (in current usage,
16081611
* they'll be results from GetCurrentTimestamp()).
16091612
*
1610-
* We expect start_time <= stop_time. If not, we return zeros; for current
1611-
* callers there is no need to be tense about which way division rounds on
1612-
* negative inputs.
1613+
* We expect start_time <= stop_time. If not, we return zeros,
1614+
* since then we're already past the previously determined stop_time.
16131615
*/
16141616
void
16151617
TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
@@ -1629,6 +1631,36 @@ TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
16291631
}
16301632
}
16311633

1634+
/*
1635+
* TimestampDifferenceMilliseconds -- convert the difference between two
1636+
* timestamps into integer milliseconds
1637+
*
1638+
* This is typically used to calculate a wait timeout for WaitLatch()
1639+
* or a related function. The choice of "long" as the result type
1640+
* is to harmonize with that. It is caller's responsibility that the
1641+
* input timestamps not be so far apart as to risk overflow of "long"
1642+
* (which'd happen at about 25 days on machines with 32-bit "long").
1643+
*
1644+
* Both inputs must be ordinary finite timestamps (in current usage,
1645+
* they'll be results from GetCurrentTimestamp()).
1646+
*
1647+
* We expect start_time <= stop_time. If not, we return zero,
1648+
* since then we're already past the previously determined stop_time.
1649+
*
1650+
* Note we round up any fractional millisecond, since waiting for just
1651+
* less than the intended timeout is undesirable.
1652+
*/
1653+
long
1654+
TimestampDifferenceMilliseconds(TimestampTz start_time, TimestampTz stop_time)
1655+
{
1656+
TimestampTz diff = stop_time - start_time;
1657+
1658+
if (diff <= 0)
1659+
return 0;
1660+
else
1661+
return (long) ((diff + 999) / 1000);
1662+
}
1663+
16321664
/*
16331665
* TimestampDifferenceExceeds -- report whether the difference between two
16341666
* timestamps is >= a threshold (expressed in milliseconds)

src/include/utils/timestamp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ extern TimestampTz GetSQLCurrentTimestamp(int32 typmod);
7272
extern Timestamp GetSQLLocalTimestamp(int32 typmod);
7373
extern void TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
7474
long *secs, int *microsecs);
75+
extern long TimestampDifferenceMilliseconds(TimestampTz start_time,
76+
TimestampTz stop_time);
7577
extern bool TimestampDifferenceExceeds(TimestampTz start_time,
7678
TimestampTz stop_time,
7779
int msec);

0 commit comments

Comments
 (0)