Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit afce790

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 19fd4f2 commit afce790

File tree

7 files changed

+80
-99
lines changed

7 files changed

+80
-99
lines changed

contrib/pg_prewarm/autoprewarm.c

+5-7
Original file line numberDiff line numberDiff line change
@@ -224,18 +224,16 @@ autoprewarm_main(Datum main_arg)
224224
}
225225
else
226226
{
227-
long delay_in_ms = 0;
228-
TimestampTz next_dump_time = 0;
229-
long secs = 0;
230-
int usecs = 0;
227+
TimestampTz next_dump_time;
228+
long delay_in_ms;
231229

232230
/* Compute the next dump time. */
233231
next_dump_time =
234232
TimestampTzPlusMilliseconds(last_dump_time,
235233
autoprewarm_interval * 1000);
236-
TimestampDifference(GetCurrentTimestamp(), next_dump_time,
237-
&secs, &usecs);
238-
delay_in_ms = secs + (usecs / 1000);
234+
delay_in_ms =
235+
TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
236+
next_dump_time);
239237

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

contrib/postgres_fdw/connection.c

+2-7
Original file line numberDiff line numberDiff line change
@@ -1196,20 +1196,15 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime, PGresult **result)
11961196
{
11971197
int wc;
11981198
TimestampTz now = GetCurrentTimestamp();
1199-
long secs;
1200-
int microsecs;
12011199
long cur_timeout;
12021200

12031201
/* If timeout has expired, give up, else get sleep time. */
1204-
if (now >= endtime)
1202+
cur_timeout = TimestampDifferenceMilliseconds(now, endtime);
1203+
if (cur_timeout <= 0)
12051204
{
12061205
timed_out = true;
12071206
goto exit;
12081207
}
1209-
TimestampDifference(now, endtime, &secs, &microsecs);
1210-
1211-
/* To protect against clock skew, limit sleep to one minute. */
1212-
cur_timeout = Min(60000, secs * USECS_PER_SEC + microsecs);
12131208

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

src/backend/access/transam/xlog.c

+31-54
Original file line numberDiff line numberDiff line change
@@ -6074,8 +6074,7 @@ recoveryApplyDelay(XLogReaderState *record)
60746074
uint8 xact_info;
60756075
TimestampTz xtime;
60766076
TimestampTz delayUntil;
6077-
long secs;
6078-
int microsecs;
6077+
long msecs;
60796078

60806079
/* nothing to do if no delay configured */
60816080
if (recovery_min_apply_delay <= 0)
@@ -6115,8 +6114,8 @@ recoveryApplyDelay(XLogReaderState *record)
61156114
* Exit without arming the latch if it's already past time to apply this
61166115
* record
61176116
*/
6118-
TimestampDifference(GetCurrentTimestamp(), delayUntil, &secs, &microsecs);
6119-
if (secs <= 0 && microsecs <= 0)
6117+
msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(), delayUntil);
6118+
if (msecs <= 0)
61206119
return false;
61216120

61226121
while (true)
@@ -6132,22 +6131,17 @@ recoveryApplyDelay(XLogReaderState *record)
61326131
/*
61336132
* Wait for difference between GetCurrentTimestamp() and delayUntil
61346133
*/
6135-
TimestampDifference(GetCurrentTimestamp(), delayUntil,
6136-
&secs, &microsecs);
6134+
msecs = TimestampDifferenceMilliseconds(GetCurrentTimestamp(),
6135+
delayUntil);
61376136

6138-
/*
6139-
* NB: We're ignoring waits below recovery_min_apply_delay's
6140-
* resolution.
6141-
*/
6142-
if (secs <= 0 && microsecs / 1000 <= 0)
6137+
if (msecs <= 0)
61436138
break;
61446139

6145-
elog(DEBUG2, "recovery apply delay %ld seconds, %d milliseconds",
6146-
secs, microsecs / 1000);
6140+
elog(DEBUG2, "recovery apply delay %ld milliseconds", msecs);
61476141

61486142
(void) WaitLatch(&XLogCtl->recoveryWakeupLatch,
61496143
WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
6150-
secs * 1000L + microsecs / 1000,
6144+
msecs,
61516145
WAIT_EVENT_RECOVERY_APPLY_DELAY);
61526146
}
61536147
return true;
@@ -8554,33 +8548,24 @@ LogCheckpointStart(int flags, bool restartpoint)
85548548
static void
85558549
LogCheckpointEnd(bool restartpoint)
85568550
{
8557-
long write_secs,
8558-
sync_secs,
8559-
total_secs,
8560-
longest_secs,
8561-
average_secs;
8562-
int write_usecs,
8563-
sync_usecs,
8564-
total_usecs,
8565-
longest_usecs,
8566-
average_usecs;
8551+
long write_msecs,
8552+
sync_msecs,
8553+
total_msecs,
8554+
longest_msecs,
8555+
average_msecs;
85678556
uint64 average_sync_time;
85688557

85698558
CheckpointStats.ckpt_end_t = GetCurrentTimestamp();
85708559

8571-
TimestampDifference(CheckpointStats.ckpt_write_t,
8572-
CheckpointStats.ckpt_sync_t,
8573-
&write_secs, &write_usecs);
8560+
write_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_write_t,
8561+
CheckpointStats.ckpt_sync_t);
85748562

8575-
TimestampDifference(CheckpointStats.ckpt_sync_t,
8576-
CheckpointStats.ckpt_sync_end_t,
8577-
&sync_secs, &sync_usecs);
8563+
sync_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_sync_t,
8564+
CheckpointStats.ckpt_sync_end_t);
85788565

85798566
/* Accumulate checkpoint timing summary data, in milliseconds. */
8580-
BgWriterStats.m_checkpoint_write_time +=
8581-
write_secs * 1000 + write_usecs / 1000;
8582-
BgWriterStats.m_checkpoint_sync_time +=
8583-
sync_secs * 1000 + sync_usecs / 1000;
8567+
BgWriterStats.m_checkpoint_write_time += write_msecs;
8568+
BgWriterStats.m_checkpoint_sync_time += sync_msecs;
85848569

85858570
/*
85868571
* All of the published timing statistics are accounted for. Only
@@ -8589,25 +8574,20 @@ LogCheckpointEnd(bool restartpoint)
85898574
if (!log_checkpoints)
85908575
return;
85918576

8592-
TimestampDifference(CheckpointStats.ckpt_start_t,
8593-
CheckpointStats.ckpt_end_t,
8594-
&total_secs, &total_usecs);
8577+
total_msecs = TimestampDifferenceMilliseconds(CheckpointStats.ckpt_start_t,
8578+
CheckpointStats.ckpt_end_t);
85958579

85968580
/*
85978581
* Timing values returned from CheckpointStats are in microseconds.
8598-
* Convert to the second plus microsecond form that TimestampDifference
8599-
* returns for homogeneous printing.
8582+
* Convert to milliseconds for consistent printing.
86008583
*/
8601-
longest_secs = (long) (CheckpointStats.ckpt_longest_sync / 1000000);
8602-
longest_usecs = CheckpointStats.ckpt_longest_sync -
8603-
(uint64) longest_secs * 1000000;
8584+
longest_msecs = (long) ((CheckpointStats.ckpt_longest_sync + 999) / 1000);
86048585

86058586
average_sync_time = 0;
86068587
if (CheckpointStats.ckpt_sync_rels > 0)
86078588
average_sync_time = CheckpointStats.ckpt_agg_sync_time /
86088589
CheckpointStats.ckpt_sync_rels;
8609-
average_secs = (long) (average_sync_time / 1000000);
8610-
average_usecs = average_sync_time - (uint64) average_secs * 1000000;
8590+
average_msecs = (long) ((average_sync_time + 999) / 1000);
86118591

86128592
elog(LOG, "%s complete: wrote %d buffers (%.1f%%); "
86138593
"%d WAL file(s) added, %d removed, %d recycled; "
@@ -8620,12 +8600,12 @@ LogCheckpointEnd(bool restartpoint)
86208600
CheckpointStats.ckpt_segs_added,
86218601
CheckpointStats.ckpt_segs_removed,
86228602
CheckpointStats.ckpt_segs_recycled,
8623-
write_secs, write_usecs / 1000,
8624-
sync_secs, sync_usecs / 1000,
8625-
total_secs, total_usecs / 1000,
8603+
write_msecs / 1000, (int) (write_msecs % 1000),
8604+
sync_msecs / 1000, (int) (sync_msecs % 1000),
8605+
total_msecs / 1000, (int) (total_msecs % 1000),
86268606
CheckpointStats.ckpt_sync_rels,
8627-
longest_secs, longest_usecs / 1000,
8628-
average_secs, average_usecs / 1000,
8607+
longest_msecs / 1000, (int) (longest_msecs % 1000),
8608+
average_msecs / 1000, (int) (average_msecs % 1000),
86298609
(int) (PrevCheckPointDistance / 1024.0),
86308610
(int) (CheckPointDistanceEstimate / 1024.0));
86318611
}
@@ -12219,13 +12199,10 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
1221912199
if (!TimestampDifferenceExceeds(last_fail_time, now,
1222012200
wal_retrieve_retry_interval))
1222112201
{
12222-
long secs,
12223-
wait_time;
12224-
int usecs;
12202+
long wait_time;
1222512203

12226-
TimestampDifference(last_fail_time, now, &secs, &usecs);
1222712204
wait_time = wal_retrieve_retry_interval -
12228-
(secs * 1000 + usecs / 1000);
12205+
TimestampDifferenceMilliseconds(last_fail_time, now);
1222912206

1223012207
(void) WaitLatch(&XLogCtl->recoveryWakeupLatch,
1223112208
WL_LATCH_SET | WL_TIMEOUT |

src/backend/replication/walreceiverfuncs.c

+4-21
Original file line numberDiff line numberDiff line change
@@ -350,10 +350,6 @@ GetReplicationApplyDelay(void)
350350
WalRcvData *walrcv = WalRcv;
351351
XLogRecPtr receivePtr;
352352
XLogRecPtr replayPtr;
353-
354-
long secs;
355-
int usecs;
356-
357353
TimestampTz chunkReplayStartTime;
358354

359355
SpinLockAcquire(&walrcv->mutex);
@@ -370,11 +366,8 @@ GetReplicationApplyDelay(void)
370366
if (chunkReplayStartTime == 0)
371367
return -1;
372368

373-
TimestampDifference(chunkReplayStartTime,
374-
GetCurrentTimestamp(),
375-
&secs, &usecs);
376-
377-
return (((int) secs * 1000) + (usecs / 1000));
369+
return TimestampDifferenceMilliseconds(chunkReplayStartTime,
370+
GetCurrentTimestamp());
378371
}
379372

380373
/*
@@ -385,24 +378,14 @@ int
385378
GetReplicationTransferLatency(void)
386379
{
387380
WalRcvData *walrcv = WalRcv;
388-
389381
TimestampTz lastMsgSendTime;
390382
TimestampTz lastMsgReceiptTime;
391383

392-
long secs = 0;
393-
int usecs = 0;
394-
int ms;
395-
396384
SpinLockAcquire(&walrcv->mutex);
397385
lastMsgSendTime = walrcv->lastMsgSendTime;
398386
lastMsgReceiptTime = walrcv->lastMsgReceiptTime;
399387
SpinLockRelease(&walrcv->mutex);
400388

401-
TimestampDifference(lastMsgSendTime,
402-
lastMsgReceiptTime,
403-
&secs, &usecs);
404-
405-
ms = ((int) secs * 1000) + (usecs / 1000);
406-
407-
return ms;
389+
return TimestampDifferenceMilliseconds(lastMsgSendTime,
390+
lastMsgReceiptTime);
408391
}

src/backend/replication/walsender.c

+1-7
Original file line numberDiff line numberDiff line change
@@ -2177,8 +2177,6 @@ WalSndComputeSleeptime(TimestampTz now)
21772177
if (wal_sender_timeout > 0 && last_reply_timestamp > 0)
21782178
{
21792179
TimestampTz wakeup_time;
2180-
long sec_to_timeout;
2181-
int microsec_to_timeout;
21822180

21832181
/*
21842182
* At the latest stop sleeping once wal_sender_timeout has been
@@ -2197,11 +2195,7 @@ WalSndComputeSleeptime(TimestampTz now)
21972195
wal_sender_timeout / 2);
21982196

21992197
/* Compute relative time until wakeup. */
2200-
TimestampDifference(now, wakeup_time,
2201-
&sec_to_timeout, &microsec_to_timeout);
2202-
2203-
sleeptime = sec_to_timeout * 1000 +
2204-
microsec_to_timeout / 1000;
2198+
sleeptime = TimestampDifferenceMilliseconds(now, wakeup_time);
22052199
}
22062200

22072201
return sleeptime;

src/backend/utils/adt/timestamp.c

+35-3
Original file line numberDiff line numberDiff line change
@@ -1637,12 +1637,14 @@ timeofday(PG_FUNCTION_ARGS)
16371637
* TimestampDifference -- convert the difference between two timestamps
16381638
* into integer seconds and microseconds
16391639
*
1640+
* This is typically used to calculate a wait timeout for select(2),
1641+
* which explains the otherwise-odd choice of output format.
1642+
*
16401643
* Both inputs must be ordinary finite timestamps (in current usage,
16411644
* they'll be results from GetCurrentTimestamp()).
16421645
*
1643-
* We expect start_time <= stop_time. If not, we return zeros; for current
1644-
* callers there is no need to be tense about which way division rounds on
1645-
* negative inputs.
1646+
* We expect start_time <= stop_time. If not, we return zeros,
1647+
* since then we're already past the previously determined stop_time.
16461648
*/
16471649
void
16481650
TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
@@ -1662,6 +1664,36 @@ TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
16621664
}
16631665
}
16641666

1667+
/*
1668+
* TimestampDifferenceMilliseconds -- convert the difference between two
1669+
* timestamps into integer milliseconds
1670+
*
1671+
* This is typically used to calculate a wait timeout for WaitLatch()
1672+
* or a related function. The choice of "long" as the result type
1673+
* is to harmonize with that. It is caller's responsibility that the
1674+
* input timestamps not be so far apart as to risk overflow of "long"
1675+
* (which'd happen at about 25 days on machines with 32-bit "long").
1676+
*
1677+
* Both inputs must be ordinary finite timestamps (in current usage,
1678+
* they'll be results from GetCurrentTimestamp()).
1679+
*
1680+
* We expect start_time <= stop_time. If not, we return zero,
1681+
* since then we're already past the previously determined stop_time.
1682+
*
1683+
* Note we round up any fractional millisecond, since waiting for just
1684+
* less than the intended timeout is undesirable.
1685+
*/
1686+
long
1687+
TimestampDifferenceMilliseconds(TimestampTz start_time, TimestampTz stop_time)
1688+
{
1689+
TimestampTz diff = stop_time - start_time;
1690+
1691+
if (diff <= 0)
1692+
return 0;
1693+
else
1694+
return (long) ((diff + 999) / 1000);
1695+
}
1696+
16651697
/*
16661698
* TimestampDifferenceExceeds -- report whether the difference between two
16671699
* timestamps is >= a threshold (expressed in milliseconds)

src/include/utils/timestamp.h

+2
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)