Implement rate-limiting logic on how often backends will attempt to send
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 30 Apr 2007 03:23:49 +0000 (03:23 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 30 Apr 2007 03:23:49 +0000 (03:23 +0000)
messages to the stats collector.  This avoids the problem that enabling
stats_row_level for autovacuum has a significant overhead for short
read-only transactions, as noted by Arjen van der Meijden.  We can avoid
an extra gettimeofday call by piggybacking on the one done for WAL-logging
xact commit or abort (although that doesn't help read-only transactions,
since they don't WAL-log anything).

In my proposal for this, I noted that we could change the WAL log entries
for commit/abort to record full TimestampTz precision, instead of only
time_t as at present.  That's not done in this patch, but will be committed
separately.

src/backend/access/transam/xact.c
src/backend/commands/analyze.c
src/backend/commands/vacuumlazy.c
src/backend/postmaster/pgstat.c
src/backend/utils/adt/timestamp.c
src/include/access/xact.h
src/include/utils/timestamp.h
src/test/regress/expected/stats.out
src/test/regress/sql/stats.sql

index 9fa18178eebbfa6eceb0f114335414679738472d..480cd88df35abbf440d3ff5bbf4b3a559781bcc4 100644 (file)
@@ -160,11 +160,13 @@ static CommandId currentCommandId;
 /*
  * xactStartTimestamp is the value of transaction_timestamp().
  * stmtStartTimestamp is the value of statement_timestamp().
+ * xactStopTimestamp is the time at which we log a commit or abort WAL record.
  * These do not change as we enter and exit subtransactions, so we don't
  * keep them inside the TransactionState stack.
  */
 static TimestampTz xactStartTimestamp;
 static TimestampTz stmtStartTimestamp;
+static TimestampTz xactStopTimestamp;
 
 /*
  * GID to be used for preparing the current transaction.  This is also
@@ -435,6 +437,20 @@ GetCurrentStatementStartTimestamp(void)
        return stmtStartTimestamp;
 }
 
+/*
+ *     GetCurrentTransactionStopTimestamp
+ *
+ * We return current time if the transaction stop time hasn't been set
+ * (which can happen if we decide we don't need to log an XLOG record).
+ */
+TimestampTz
+GetCurrentTransactionStopTimestamp(void)
+{
+       if (xactStopTimestamp != 0)
+               return xactStopTimestamp;
+       return GetCurrentTimestamp();
+}
+
 /*
  *     SetCurrentStatementStartTimestamp
  */
@@ -444,6 +460,15 @@ SetCurrentStatementStartTimestamp(void)
        stmtStartTimestamp = GetCurrentTimestamp();
 }
 
+/*
+ *     SetCurrentTransactionStopTimestamp
+ */
+static inline void
+SetCurrentTransactionStopTimestamp(void)
+{
+       xactStopTimestamp = GetCurrentTimestamp();
+}
+
 /*
  *     GetCurrentTransactionNestLevel
  *
@@ -747,7 +772,8 @@ RecordTransactionCommit(void)
                         */
                        MyProc->inCommit = true;
 
-                       xlrec.xtime = time(NULL);
+                       SetCurrentTransactionStopTimestamp();
+                       xlrec.xtime = timestamptz_to_time_t(xactStopTimestamp);
                        xlrec.nrels = nrels;
                        xlrec.nsubxacts = nchildren;
                        rdata[0].data = (char *) (&xlrec);
@@ -1042,7 +1068,8 @@ RecordTransactionAbort(void)
                        xl_xact_abort xlrec;
                        XLogRecPtr      recptr;
 
-                       xlrec.xtime = time(NULL);
+                       SetCurrentTransactionStopTimestamp();
+                       xlrec.xtime = timestamptz_to_time_t(xactStopTimestamp);
                        xlrec.nrels = nrels;
                        xlrec.nsubxacts = nchildren;
                        rdata[0].data = (char *) (&xlrec);
@@ -1415,9 +1442,11 @@ StartTransaction(void)
        /*
         * set transaction_timestamp() (a/k/a now()).  We want this to be the same
         * as the first command's statement_timestamp(), so don't do a fresh
-        * GetCurrentTimestamp() call (which'd be expensive anyway).
+        * GetCurrentTimestamp() call (which'd be expensive anyway).  Also,
+        * mark xactStopTimestamp as unset.
         */
        xactStartTimestamp = stmtStartTimestamp;
+       xactStopTimestamp = 0;
        pgstat_report_txn_timestamp(xactStartTimestamp);
 
        /*
index 826999800062eca1ee8ccf36ba0b82ffe5fb5813..0bd8a6c9b9555731440b1aed1a64262f868e5fa6 100644 (file)
@@ -468,29 +468,15 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
        /* Log the action if appropriate */
        if (IsAutoVacuumWorkerProcess() && Log_autovacuum >= 0)
        {
-               long    diff = 0L;
-
-               if (Log_autovacuum > 0)
-               {
-                       TimestampTz     endtime;
-                       int             usecs;
-                       long    secs;
-
-                       endtime = GetCurrentTimestamp();
-                       TimestampDifference(starttime, endtime, &secs, &usecs);
-
-                       diff = secs * 1000 + usecs / 1000;
-               }
-               
-               if (Log_autovacuum == 0 || diff >= Log_autovacuum)
-               {
+               if (Log_autovacuum == 0 ||
+                       TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
+                                                                          Log_autovacuum))
                        ereport(LOG,
                                        (errmsg("automatic analyze of table \"%s.%s.%s\" system usage: %s",
                                                        get_database_name(MyDatabaseId),
                                                        get_namespace_name(RelationGetNamespace(onerel)),
                                                        RelationGetRelationName(onerel),
                                                        pg_rusage_show(&ru0))));
-               }
        }
 }
 
index 876393d0ec648dce226221d36212823d8cfdb205..d9a5ab7eac7fcf09e407043889f5e27e910f9376 100644 (file)
@@ -217,22 +217,9 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
        /* and log the action if appropriate */
        if (IsAutoVacuumWorkerProcess() && Log_autovacuum >= 0)
        {
-               long    diff = 0L;
-
-               if (Log_autovacuum > 0)
-               {
-                       TimestampTz     endtime;
-                       int             usecs;
-                       long    secs;
-
-                       endtime = GetCurrentTimestamp();
-                       TimestampDifference(starttime, endtime, &secs, &usecs);
-
-                       diff = secs * 1000 + usecs / 1000;
-               }
-               
-               if (Log_autovacuum == 0 || diff >= Log_autovacuum)
-               {
+               if (Log_autovacuum == 0 ||
+                       TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
+                                                                          Log_autovacuum))
                        ereport(LOG,
                                        (errmsg("automatic vacuum of table \"%s.%s.%s\": index scans: %d\n"
                                                        "pages: %d removed, %d remain\n"
@@ -245,7 +232,6 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
                                                        vacrelstats->pages_removed, vacrelstats->rel_pages,
                                                        vacrelstats->tuples_deleted, vacrelstats->rel_tuples, 
                                                        pg_rusage_show(&ru0))));
-               }
        }
 }
 
index 96f6022fbb3cb2a04b1040052f97f0080b1b4793..43b64555d85abca2acbcf0b522928b6c095d7ac3 100644 (file)
@@ -608,15 +608,34 @@ void allow_immediate_pgstat_restart(void)
 /* ----------
  * pgstat_report_tabstat() -
  *
- *     Called from tcop/postgres.c to send the so far collected
- *     per table access statistics to the collector.
+ *     Called from tcop/postgres.c to send the so far collected per-table
+ *     access statistics to the collector.  Note that this is called only
+ *     when not within a transaction, so it is fair to use transaction stop
+ *     time as an approximation of current time.
  * ----------
  */
 void
 pgstat_report_tabstat(void)
 {
+       static TimestampTz last_report = 0;     
+       TimestampTz now;
+
+       /* Don't expend a clock check if nothing to do */
+       if (RegularTabStat.tsa_used == 0 &&
+               SharedTabStat.tsa_used == 0)
+               return;
+
+       /*
+        * Don't send a message unless it's been at least PGSTAT_STAT_INTERVAL
+        * msec since we last sent one.
+        */
+       now = GetCurrentTransactionStopTimestamp();
+       if (!TimestampDifferenceExceeds(last_report, now, PGSTAT_STAT_INTERVAL))
+               return;
+       last_report = now;
+
        /*
-        * For each message buffer used during the last query set the header
+        * For each message buffer used during the last queries, set the header
         * fields and send it out; then mark the entries unused.
         */
        pgstat_report_one_tabstat(&RegularTabStat, MyDatabaseId);
index 1d1917ced4685ba3f02a91a6064246fb3eea6396..420249736f7493c4754e734fb02f366bcd6d6396 100644 (file)
@@ -1238,6 +1238,27 @@ TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
        }
 }
 
+/*
+ * TimestampDifferenceExceeds -- report whether the difference between two
+ *             timestamps is >= a threshold (expressed in milliseconds)
+ *
+ * Both inputs must be ordinary finite timestamps (in current usage,
+ * they'll be results from GetCurrentTimestamp()).
+ */
+bool
+TimestampDifferenceExceeds(TimestampTz start_time,
+                                                  TimestampTz stop_time,
+                                                  int msec)
+{
+       TimestampTz diff = stop_time - start_time;
+
+#ifdef HAVE_INT64_TIMESTAMP
+       return (diff >= msec * INT64CONST(1000));
+#else
+       return (diff * 1000.0 >= msec);
+#endif
+}
+
 /*
  * Convert a time_t to TimestampTz.
  *
index e43c94654d5dcb1a6c676c8d45c189025253a569..cb9188de577e87f7938e98db5fe0887f2884f5ca 100644 (file)
@@ -142,6 +142,7 @@ extern SubTransactionId GetCurrentSubTransactionId(void);
 extern CommandId GetCurrentCommandId(void);
 extern TimestampTz GetCurrentTransactionStartTimestamp(void);
 extern TimestampTz GetCurrentStatementStartTimestamp(void);
+extern TimestampTz GetCurrentTransactionStopTimestamp(void);
 extern void SetCurrentStatementStartTimestamp(void);
 extern int     GetCurrentTransactionNestLevel(void);
 extern bool TransactionIdIsCurrentTransactionId(TransactionId xid);
index 93bb459346409baffcd8e28c3e1773c1565aed49..a019a309edf3ee7584cc82d19ba21391fe71748c 100644 (file)
@@ -311,6 +311,9 @@ extern TimestampTz GetCurrentTimestamp(void);
 
 extern void TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
                                        long *secs, int *microsecs);
+extern bool TimestampDifferenceExceeds(TimestampTz start_time,
+                                                                          TimestampTz stop_time,
+                                                                          int msec);
 
 extern TimestampTz time_t_to_timestamptz(time_t tm);
 extern time_t timestamptz_to_time_t(TimestampTz t);
index fc76d1422599b375f51ed488bc61b86d173cade2..af0f630a8da981310a12db1ff93dbd279e063307 100644 (file)
@@ -73,6 +73,14 @@ SELECT count(*) FROM tenk2 WHERE unique1 = 1;
      1
 (1 row)
 
+-- force the rate-limiting logic in pgstat_report_tabstat() to time out
+-- and send a message
+SELECT pg_sleep(1.0);
+ pg_sleep 
+----------
+(1 row)
+
 -- wait for stats collector to update
 SELECT wait_for_stats();
  wait_for_stats 
index cde38b3a379e5310f57fe3392625d17fdaa16260..9900b9114a50eb3d77586adc60bc54232b9d68e0 100644 (file)
@@ -60,6 +60,10 @@ SELECT count(*) FROM tenk2;
 -- do an indexscan
 SELECT count(*) FROM tenk2 WHERE unique1 = 1;
 
+-- force the rate-limiting logic in pgstat_report_tabstat() to time out
+-- and send a message
+SELECT pg_sleep(1.0);
+
 -- wait for stats collector to update
 SELECT wait_for_stats();