Revise pgstat's tracking of tuple changes to improve the reliability of
authorTom Lane <[email protected]>
Wed, 30 Dec 2009 20:32:14 +0000 (20:32 +0000)
committerTom Lane <[email protected]>
Wed, 30 Dec 2009 20:32:14 +0000 (20:32 +0000)
decisions about when to auto-analyze.

The previous code depended on n_live_tuples + n_dead_tuples - last_anl_tuples,
where all three of these numbers could be bad estimates from ANALYZE itself.
Even worse, in the presence of a steady flow of HOT updates and matching
HOT-tuple reclamations, auto-analyze might never trigger at all, even if all
three numbers are exactly right, because n_dead_tuples could hold steady.

To fix, replace last_anl_tuples with an accurately tracked count of the total
number of committed tuple inserts + updates + deletes since the last ANALYZE
on the table.  This can still be compared to the same threshold as before, but
it's much more trustworthy than the old computation.  Tracking this requires
one more intra-transaction counter per modified table within backends, but no
additional memory space in the stats collector.  There probably isn't any
measurable speed difference; if anything it might be a bit faster than before,
since I was able to eliminate some per-tuple arithmetic operations in favor of
adding sums once per (sub)transaction.

Also, simplify the logic around pgstat vacuum and analyze reporting messages
by not trying to fold VACUUM ANALYZE into a single pgstat message.

The original thought behind this patch was to allow scheduling of analyzes
on parent tables by artificially inflating their changes_since_analyze count.
I've left that for a separate patch since this change seems to stand on its
own merit.

src/backend/commands/analyze.c
src/backend/commands/vacuum.c
src/backend/commands/vacuumlazy.c
src/backend/postmaster/autovacuum.c
src/backend/postmaster/pgstat.c
src/include/pgstat.h

index 251d42c509722507f28ac6682e505f964ab76229..ee2d4ee250c057e3d79fea2e300c3cd0b96ab107 100644 (file)
@@ -535,16 +535,13 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
    }
 
    /*
-    * Update pages/tuples stats in pg_class.
+    * Update pages/tuples stats in pg_class, but not if we're inside a
+    * VACUUM that got a more precise number.
     */
    if (update_reltuples)
-   {
        vac_update_relstats(onerel,
                            RelationGetNumberOfBlocks(onerel),
                            totalrows, hasindex, InvalidTransactionId);
-       /* report results to the stats collector, too */
-       pgstat_report_analyze(onerel, totalrows, totaldeadrows);
-   }
 
    /*
     * Same for indexes. Vacuum always scans all indexes, so if we're part of
@@ -565,6 +562,13 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
        }
    }
 
+   /*
+    * Report ANALYZE to the stats collector, too; likewise, tell it to
+    * adopt these numbers only if we're not inside a VACUUM that got a
+    * better number.
+    */
+   pgstat_report_analyze(onerel, update_reltuples, totalrows, totaldeadrows);
+
    /* We skip to here if there were no analyzable columns */
 cleanup:
 
index f3cdb92b9195af9578886b854218fb3b896ca277..d1e7c1f6c46d7179fbc582b41cdc810ba4528faf 100644 (file)
@@ -1332,7 +1332,6 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
    pgstat_report_vacuum(RelationGetRelid(onerel),
                         onerel->rd_rel->relisshared,
                         true,
-                        (vacstmt->options & VACOPT_ANALYZE) != 0,
                         vacrelstats->rel_tuples);
 
    return heldoff;
index ca35eade5d1bccca8339f8300c1aab9faafa2403..b2f7c69156697dd8b5b631d3143d90a7b0525f49 100644 (file)
@@ -237,7 +237,6 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
    pgstat_report_vacuum(RelationGetRelid(onerel),
                         onerel->rd_rel->relisshared,
                         vacrelstats->scanned_all,
-                        (vacstmt->options & VACOPT_ANALYZE) != 0,
                         vacrelstats->rel_tuples);
 
    /* and log the action if appropriate */
index 10b864d6b2321b812ded5296fd3c75570d88b5d6..4fde74d6efbd7e1f0800f2b377969e70e4f9cb00 100644 (file)
@@ -2591,8 +2591,7 @@ relation_needs_vacanalyze(Oid relid,
    {
        reltuples = classForm->reltuples;
        vactuples = tabentry->n_dead_tuples;
-       anltuples = tabentry->n_live_tuples + tabentry->n_dead_tuples -
-           tabentry->last_anl_tuples;
+       anltuples = tabentry->changes_since_analyze;
 
        vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples;
        anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
index e959a23fbed1d482f8a2f928202d3d05b539e0f3..5f249197b563f6323a204b6e403edbb5fbc0cab9 100644 (file)
@@ -195,6 +195,7 @@ static int  pgStatXactRollback = 0;
 typedef struct TwoPhasePgStatRecord
 {
    PgStat_Counter tuples_inserted;     /* tuples inserted in xact */
+   PgStat_Counter tuples_updated;      /* tuples updated in xact */
    PgStat_Counter tuples_deleted;      /* tuples deleted in xact */
    Oid         t_id;           /* table's OID */
    bool        t_shared;       /* is it a shared catalog? */
@@ -1184,8 +1185,8 @@ pgstat_report_autovac(Oid dboid)
  * ---------
  */
 void
-pgstat_report_vacuum(Oid tableoid, bool shared, bool scanned_all,
-                    bool analyze, PgStat_Counter tuples)
+pgstat_report_vacuum(Oid tableoid, bool shared, bool adopt_counts,
+                    PgStat_Counter tuples)
 {
    PgStat_MsgVacuum msg;
 
@@ -1195,9 +1196,8 @@ pgstat_report_vacuum(Oid tableoid, bool shared, bool scanned_all,
    pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM);
    msg.m_databaseid = shared ? InvalidOid : MyDatabaseId;
    msg.m_tableoid = tableoid;
-   msg.m_scanned_all = scanned_all;
-   msg.m_analyze = analyze;
-   msg.m_autovacuum = IsAutoVacuumWorkerProcess();     /* is this autovacuum? */
+   msg.m_adopt_counts = adopt_counts;
+   msg.m_autovacuum = IsAutoVacuumWorkerProcess();
    msg.m_vacuumtime = GetCurrentTimestamp();
    msg.m_tuples = tuples;
    pgstat_send(&msg, sizeof(msg));
@@ -1210,8 +1210,8 @@ pgstat_report_vacuum(Oid tableoid, bool shared, bool scanned_all,
  * --------
  */
 void
-pgstat_report_analyze(Relation rel, PgStat_Counter livetuples,
-                     PgStat_Counter deadtuples)
+pgstat_report_analyze(Relation rel, bool adopt_counts,
+                     PgStat_Counter livetuples, PgStat_Counter deadtuples)
 {
    PgStat_MsgAnalyze msg;
 
@@ -1235,10 +1235,10 @@ pgstat_report_analyze(Relation rel, PgStat_Counter livetuples,
        for (trans = rel->pgstat_info->trans; trans; trans = trans->upper)
        {
            livetuples -= trans->tuples_inserted - trans->tuples_deleted;
-           deadtuples -= trans->tuples_deleted;
+           deadtuples -= trans->tuples_updated + trans->tuples_deleted;
        }
        /* count stuff inserted by already-aborted subxacts, too */
-       deadtuples -= rel->pgstat_info->t_counts.t_new_dead_tuples;
+       deadtuples -= rel->pgstat_info->t_counts.t_delta_dead_tuples;
        /* Since ANALYZE's counts are estimates, we could have underflowed */
        livetuples = Max(livetuples, 0);
        deadtuples = Max(deadtuples, 0);
@@ -1247,7 +1247,8 @@ pgstat_report_analyze(Relation rel, PgStat_Counter livetuples,
    pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE);
    msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId;
    msg.m_tableoid = RelationGetRelid(rel);
-   msg.m_autovacuum = IsAutoVacuumWorkerProcess();     /* is this autovacuum? */
+   msg.m_adopt_counts = adopt_counts;
+   msg.m_autovacuum = IsAutoVacuumWorkerProcess();
    msg.m_analyzetime = GetCurrentTimestamp();
    msg.m_live_tuples = livetuples;
    msg.m_dead_tuples = deadtuples;
@@ -1559,12 +1560,9 @@ pgstat_count_heap_insert(Relation rel)
 
    if (pgstat_track_counts && pgstat_info != NULL)
    {
+       /* We have to log the effect at the proper transactional level */
        int         nest_level = GetCurrentTransactionNestLevel();
 
-       /* t_tuples_inserted is nontransactional, so just advance it */
-       pgstat_info->t_counts.t_tuples_inserted++;
-
-       /* We have to log the transactional effect at the proper level */
        if (pgstat_info->trans == NULL ||
            pgstat_info->trans->nest_level != nest_level)
            add_tabstat_xact_level(pgstat_info, nest_level);
@@ -1583,22 +1581,18 @@ pgstat_count_heap_update(Relation rel, bool hot)
 
    if (pgstat_track_counts && pgstat_info != NULL)
    {
+       /* We have to log the effect at the proper transactional level */
        int         nest_level = GetCurrentTransactionNestLevel();
 
-       /* t_tuples_updated is nontransactional, so just advance it */
-       pgstat_info->t_counts.t_tuples_updated++;
-       /* ditto for the hot_update counter */
-       if (hot)
-           pgstat_info->t_counts.t_tuples_hot_updated++;
-
-       /* We have to log the transactional effect at the proper level */
        if (pgstat_info->trans == NULL ||
            pgstat_info->trans->nest_level != nest_level)
            add_tabstat_xact_level(pgstat_info, nest_level);
 
-       /* An UPDATE both inserts a new tuple and deletes the old */
-       pgstat_info->trans->tuples_inserted++;
-       pgstat_info->trans->tuples_deleted++;
+       pgstat_info->trans->tuples_updated++;
+
+       /* t_tuples_hot_updated is nontransactional, so just advance it */
+       if (hot)
+           pgstat_info->t_counts.t_tuples_hot_updated++;
    }
 }
 
@@ -1612,12 +1606,9 @@ pgstat_count_heap_delete(Relation rel)
 
    if (pgstat_track_counts && pgstat_info != NULL)
    {
+       /* We have to log the effect at the proper transactional level */
        int         nest_level = GetCurrentTransactionNestLevel();
 
-       /* t_tuples_deleted is nontransactional, so just advance it */
-       pgstat_info->t_counts.t_tuples_deleted++;
-
-       /* We have to log the transactional effect at the proper level */
        if (pgstat_info->trans == NULL ||
            pgstat_info->trans->nest_level != nest_level)
            add_tabstat_xact_level(pgstat_info, nest_level);
@@ -1630,7 +1621,7 @@ pgstat_count_heap_delete(Relation rel)
  * pgstat_update_heap_dead_tuples - update dead-tuples count
  *
  * The semantics of this are that we are reporting the nontransactional
- * recovery of "delta" dead tuples; so t_new_dead_tuples decreases
+ * recovery of "delta" dead tuples; so t_delta_dead_tuples decreases
  * rather than increasing, and the change goes straight into the per-table
  * counter, not into transactional state.
  */
@@ -1640,7 +1631,7 @@ pgstat_update_heap_dead_tuples(Relation rel, int delta)
    PgStat_TableStatus *pgstat_info = rel->pgstat_info;
 
    if (pgstat_track_counts && pgstat_info != NULL)
-       pgstat_info->t_counts.t_new_dead_tuples -= delta;
+       pgstat_info->t_counts.t_delta_dead_tuples -= delta;
 }
 
 
@@ -1684,16 +1675,29 @@ AtEOXact_PgStat(bool isCommit)
            Assert(trans->upper == NULL);
            tabstat = trans->parent;
            Assert(tabstat->trans == trans);
+           /* count attempted actions regardless of commit/abort */
+           tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
+           tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
+           tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
            if (isCommit)
            {
-               tabstat->t_counts.t_new_live_tuples +=
+               /* insert adds a live tuple, delete removes one */
+               tabstat->t_counts.t_delta_live_tuples +=
                    trans->tuples_inserted - trans->tuples_deleted;
-               tabstat->t_counts.t_new_dead_tuples += trans->tuples_deleted;
+               /* update and delete each create a dead tuple */
+               tabstat->t_counts.t_delta_dead_tuples +=
+                   trans->tuples_updated + trans->tuples_deleted;
+               /* insert, update, delete each count as one change event */
+               tabstat->t_counts.t_changed_tuples +=
+                   trans->tuples_inserted + trans->tuples_updated +
+                   trans->tuples_deleted;
            }
            else
            {
                /* inserted tuples are dead, deleted tuples are unaffected */
-               tabstat->t_counts.t_new_dead_tuples += trans->tuples_inserted;
+               tabstat->t_counts.t_delta_dead_tuples +=
+                   trans->tuples_inserted + trans->tuples_updated;
+               /* an aborted xact generates no changed_tuple events */
            }
            tabstat->trans = NULL;
        }
@@ -1742,6 +1746,7 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
                if (trans->upper && trans->upper->nest_level == nestDepth - 1)
                {
                    trans->upper->tuples_inserted += trans->tuples_inserted;
+                   trans->upper->tuples_updated += trans->tuples_updated;
                    trans->upper->tuples_deleted += trans->tuples_deleted;
                    tabstat->trans = trans->upper;
                    pfree(trans);
@@ -1767,10 +1772,17 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
            else
            {
                /*
-                * On abort, inserted tuples are dead (and can be bounced out
-                * to the top-level tabstat), deleted tuples are unaffected
+                * On abort, update top-level tabstat counts, then forget
+                * the subtransaction
                 */
-               tabstat->t_counts.t_new_dead_tuples += trans->tuples_inserted;
+
+               /* count attempted actions regardless of commit/abort */
+               tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
+               tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
+               tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
+               /* inserted tuples are dead, deleted tuples are unaffected */
+               tabstat->t_counts.t_delta_dead_tuples +=
+                   trans->tuples_inserted + trans->tuples_updated;
                tabstat->trans = trans->upper;
                pfree(trans);
            }
@@ -1810,6 +1822,7 @@ AtPrepare_PgStat(void)
            Assert(tabstat->trans == trans);
 
            record.tuples_inserted = trans->tuples_inserted;
+           record.tuples_updated = trans->tuples_updated;
            record.tuples_deleted = trans->tuples_deleted;
            record.t_id = tabstat->t_id;
            record.t_shared = tabstat->t_shared;
@@ -1874,9 +1887,17 @@ pgstat_twophase_postcommit(TransactionId xid, uint16 info,
    /* Find or create a tabstat entry for the rel */
    pgstat_info = get_tabstat_entry(rec->t_id, rec->t_shared);
 
-   pgstat_info->t_counts.t_new_live_tuples +=
+   /* Same math as in AtEOXact_PgStat, commit case */
+   pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
+   pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
+   pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
+   pgstat_info->t_counts.t_delta_live_tuples +=
        rec->tuples_inserted - rec->tuples_deleted;
-   pgstat_info->t_counts.t_new_dead_tuples += rec->tuples_deleted;
+   pgstat_info->t_counts.t_delta_dead_tuples +=
+       rec->tuples_updated + rec->tuples_deleted;
+   pgstat_info->t_counts.t_changed_tuples +=
+       rec->tuples_inserted + rec->tuples_updated +
+       rec->tuples_deleted;
 }
 
 /*
@@ -1895,8 +1916,12 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
    /* Find or create a tabstat entry for the rel */
    pgstat_info = get_tabstat_entry(rec->t_id, rec->t_shared);
 
-   /* inserted tuples are dead, deleted tuples are no-ops */
-   pgstat_info->t_counts.t_new_dead_tuples += rec->tuples_inserted;
+   /* Same math as in AtEOXact_PgStat, abort case */
+   pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
+   pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
+   pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
+   pgstat_info->t_counts.t_delta_dead_tuples +=
+       rec->tuples_inserted + rec->tuples_updated;
 }
 
 
@@ -3045,7 +3070,7 @@ pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry, Oid tableoid, bool create)
        result->tuples_hot_updated = 0;
        result->n_live_tuples = 0;
        result->n_dead_tuples = 0;
-       result->last_anl_tuples = 0;
+       result->changes_since_analyze = 0;
        result->blocks_fetched = 0;
        result->blocks_hit = 0;
 
@@ -3634,7 +3659,6 @@ pgstat_recv_inquiry(PgStat_MsgInquiry *msg, int len)
 static void
 pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
 {
-   PgStat_TableEntry *tabmsg = &(msg->m_entry[0]);
    PgStat_StatDBEntry *dbentry;
    PgStat_StatTabEntry *tabentry;
    int         i;
@@ -3653,8 +3677,10 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
     */
    for (i = 0; i < msg->m_nentries; i++)
    {
+       PgStat_TableEntry *tabmsg = &(msg->m_entry[i]);
+
        tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
-                                                 (void *) &(tabmsg[i].t_id),
+                                                      (void *) &(tabmsg->t_id),
                                                       HASH_ENTER, &found);
 
        if (!found)
@@ -3663,18 +3689,18 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
             * If it's a new table entry, initialize counters to the values we
             * just got.
             */
-           tabentry->numscans = tabmsg[i].t_counts.t_numscans;
-           tabentry->tuples_returned = tabmsg[i].t_counts.t_tuples_returned;
-           tabentry->tuples_fetched = tabmsg[i].t_counts.t_tuples_fetched;
-           tabentry->tuples_inserted = tabmsg[i].t_counts.t_tuples_inserted;
-           tabentry->tuples_updated = tabmsg[i].t_counts.t_tuples_updated;
-           tabentry->tuples_deleted = tabmsg[i].t_counts.t_tuples_deleted;
-           tabentry->tuples_hot_updated = tabmsg[i].t_counts.t_tuples_hot_updated;
-           tabentry->n_live_tuples = tabmsg[i].t_counts.t_new_live_tuples;
-           tabentry->n_dead_tuples = tabmsg[i].t_counts.t_new_dead_tuples;
-           tabentry->last_anl_tuples = 0;
-           tabentry->blocks_fetched = tabmsg[i].t_counts.t_blocks_fetched;
-           tabentry->blocks_hit = tabmsg[i].t_counts.t_blocks_hit;
+           tabentry->numscans = tabmsg->t_counts.t_numscans;
+           tabentry->tuples_returned = tabmsg->t_counts.t_tuples_returned;
+           tabentry->tuples_fetched = tabmsg->t_counts.t_tuples_fetched;
+           tabentry->tuples_inserted = tabmsg->t_counts.t_tuples_inserted;
+           tabentry->tuples_updated = tabmsg->t_counts.t_tuples_updated;
+           tabentry->tuples_deleted = tabmsg->t_counts.t_tuples_deleted;
+           tabentry->tuples_hot_updated = tabmsg->t_counts.t_tuples_hot_updated;
+           tabentry->n_live_tuples = tabmsg->t_counts.t_delta_live_tuples;
+           tabentry->n_dead_tuples = tabmsg->t_counts.t_delta_dead_tuples;
+           tabentry->changes_since_analyze = tabmsg->t_counts.t_changed_tuples;
+           tabentry->blocks_fetched = tabmsg->t_counts.t_blocks_fetched;
+           tabentry->blocks_hit = tabmsg->t_counts.t_blocks_hit;
 
            tabentry->vacuum_timestamp = 0;
            tabentry->autovac_vacuum_timestamp = 0;
@@ -3686,20 +3712,21 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
            /*
             * Otherwise add the values to the existing entry.
             */
-           tabentry->numscans += tabmsg[i].t_counts.t_numscans;
-           tabentry->tuples_returned += tabmsg[i].t_counts.t_tuples_returned;
-           tabentry->tuples_fetched += tabmsg[i].t_counts.t_tuples_fetched;
-           tabentry->tuples_inserted += tabmsg[i].t_counts.t_tuples_inserted;
-           tabentry->tuples_updated += tabmsg[i].t_counts.t_tuples_updated;
-           tabentry->tuples_deleted += tabmsg[i].t_counts.t_tuples_deleted;
-           tabentry->tuples_hot_updated += tabmsg[i].t_counts.t_tuples_hot_updated;
-           tabentry->n_live_tuples += tabmsg[i].t_counts.t_new_live_tuples;
-           tabentry->n_dead_tuples += tabmsg[i].t_counts.t_new_dead_tuples;
-           tabentry->blocks_fetched += tabmsg[i].t_counts.t_blocks_fetched;
-           tabentry->blocks_hit += tabmsg[i].t_counts.t_blocks_hit;
+           tabentry->numscans += tabmsg->t_counts.t_numscans;
+           tabentry->tuples_returned += tabmsg->t_counts.t_tuples_returned;
+           tabentry->tuples_fetched += tabmsg->t_counts.t_tuples_fetched;
+           tabentry->tuples_inserted += tabmsg->t_counts.t_tuples_inserted;
+           tabentry->tuples_updated += tabmsg->t_counts.t_tuples_updated;
+           tabentry->tuples_deleted += tabmsg->t_counts.t_tuples_deleted;
+           tabentry->tuples_hot_updated += tabmsg->t_counts.t_tuples_hot_updated;
+           tabentry->n_live_tuples += tabmsg->t_counts.t_delta_live_tuples;
+           tabentry->n_dead_tuples += tabmsg->t_counts.t_delta_dead_tuples;
+           tabentry->changes_since_analyze += tabmsg->t_counts.t_changed_tuples;
+           tabentry->blocks_fetched += tabmsg->t_counts.t_blocks_fetched;
+           tabentry->blocks_hit += tabmsg->t_counts.t_blocks_hit;
        }
 
-       /* Clamp n_live_tuples in case of negative new_live_tuples */
+       /* Clamp n_live_tuples in case of negative delta_live_tuples */
        tabentry->n_live_tuples = Max(tabentry->n_live_tuples, 0);
        /* Likewise for n_dead_tuples */
        tabentry->n_dead_tuples = Max(tabentry->n_dead_tuples, 0);
@@ -3707,13 +3734,13 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
        /*
         * Add per-table stats to the per-database entry, too.
         */
-       dbentry->n_tuples_returned += tabmsg[i].t_counts.t_tuples_returned;
-       dbentry->n_tuples_fetched += tabmsg[i].t_counts.t_tuples_fetched;
-       dbentry->n_tuples_inserted += tabmsg[i].t_counts.t_tuples_inserted;
-       dbentry->n_tuples_updated += tabmsg[i].t_counts.t_tuples_updated;
-       dbentry->n_tuples_deleted += tabmsg[i].t_counts.t_tuples_deleted;
-       dbentry->n_blocks_fetched += tabmsg[i].t_counts.t_blocks_fetched;
-       dbentry->n_blocks_hit += tabmsg[i].t_counts.t_blocks_hit;
+       dbentry->n_tuples_returned += tabmsg->t_counts.t_tuples_returned;
+       dbentry->n_tuples_fetched += tabmsg->t_counts.t_tuples_fetched;
+       dbentry->n_tuples_inserted += tabmsg->t_counts.t_tuples_inserted;
+       dbentry->n_tuples_updated += tabmsg->t_counts.t_tuples_updated;
+       dbentry->n_tuples_deleted += tabmsg->t_counts.t_tuples_deleted;
+       dbentry->n_blocks_fetched += tabmsg->t_counts.t_blocks_fetched;
+       dbentry->n_blocks_hit += tabmsg->t_counts.t_blocks_hit;
    }
 }
 
@@ -3879,36 +3906,15 @@ pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len)
 
    tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
 
-   if (msg->m_autovacuum)
-       tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
-   else
-       tabentry->vacuum_timestamp = msg->m_vacuumtime;
-   if (msg->m_scanned_all)
+   if (msg->m_adopt_counts)
        tabentry->n_live_tuples = msg->m_tuples;
    /* Resetting dead_tuples to 0 is an approximation ... */
    tabentry->n_dead_tuples = 0;
-   if (msg->m_analyze)
-   {
-       if (msg->m_scanned_all)
-           tabentry->last_anl_tuples = msg->m_tuples;
-       else
-       {
-           /* last_anl_tuples must never exceed n_live_tuples+n_dead_tuples */
-           tabentry->last_anl_tuples = Min(tabentry->last_anl_tuples,
-                                           tabentry->n_live_tuples);
-       }
 
-       if (msg->m_autovacuum)
-           tabentry->autovac_analyze_timestamp = msg->m_vacuumtime;
-       else
-           tabentry->analyze_timestamp = msg->m_vacuumtime;
-   }
+   if (msg->m_autovacuum)
+       tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
    else
-   {
-       /* last_anl_tuples must never exceed n_live_tuples+n_dead_tuples */
-       tabentry->last_anl_tuples = Min(tabentry->last_anl_tuples,
-                                       tabentry->n_live_tuples);
-   }
+       tabentry->vacuum_timestamp = msg->m_vacuumtime;
 }
 
 /* ----------
@@ -3930,13 +3936,22 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
 
    tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
 
+   if (msg->m_adopt_counts)
+   {
+       tabentry->n_live_tuples = msg->m_live_tuples;
+       tabentry->n_dead_tuples = msg->m_dead_tuples;
+   }
+
+   /*
+    * We reset changes_since_analyze to zero, forgetting any changes that
+    * occurred while the ANALYZE was in progress.
+    */
+   tabentry->changes_since_analyze = 0;
+
    if (msg->m_autovacuum)
        tabentry->autovac_analyze_timestamp = msg->m_analyzetime;
    else
        tabentry->analyze_timestamp = msg->m_analyzetime;
-   tabentry->n_live_tuples = msg->m_live_tuples;
-   tabentry->n_dead_tuples = msg->m_dead_tuples;
-   tabentry->last_anl_tuples = msg->m_live_tuples + msg->m_dead_tuples;
 }
 
 
index 102507d2b332111f516ad3de1cff781baf08b6c2..6322463ec9d678bea366c5104b70f76d1916b74c 100644 (file)
@@ -68,9 +68,9 @@ typedef int64 PgStat_Counter;
  * fetched by heap_fetch under the control of simple indexscans for this index.
  *
  * tuples_inserted/updated/deleted/hot_updated count attempted actions,
- * regardless of whether the transaction committed.  new_live_tuples and
- * new_dead_tuples are properly adjusted depending on commit or abort.
- * Note that new_live_tuples and new_dead_tuples can be negative!
+ * regardless of whether the transaction committed.  delta_live_tuples,
+ * delta_dead_tuples, and changed_tuples are set depending on commit or abort.
+ * Note that delta_live_tuples and delta_dead_tuples can be negative!
  * ----------
  */
 typedef struct PgStat_TableCounts
@@ -85,8 +85,9 @@ typedef struct PgStat_TableCounts
    PgStat_Counter t_tuples_deleted;
    PgStat_Counter t_tuples_hot_updated;
 
-   PgStat_Counter t_new_live_tuples;
-   PgStat_Counter t_new_dead_tuples;
+   PgStat_Counter t_delta_live_tuples;
+   PgStat_Counter t_delta_dead_tuples;
+   PgStat_Counter t_changed_tuples;
 
    PgStat_Counter t_blocks_fetched;
    PgStat_Counter t_blocks_hit;
@@ -102,14 +103,14 @@ typedef struct PgStat_TableCounts
 /* ----------
  * PgStat_TableStatus          Per-table status within a backend
  *
- * Most of the event counters are nontransactional, ie, we count events
+ * Many of the event counters are nontransactional, ie, we count events
  * in committed and aborted transactions alike.  For these, we just count
- * directly in the PgStat_TableStatus. However, new_live_tuples and
- * new_dead_tuples must be derived from tuple insertion and deletion counts
+ * directly in the PgStat_TableStatus. However, delta_live_tuples,
+ * delta_dead_tuples, and changed_tuples must be derived from event counts
  * with awareness of whether the transaction or subtransaction committed or
  * aborted.  Hence, we also keep a stack of per-(sub)transaction status
  * records for every table modified in the current transaction.  At commit
- * or abort, we propagate tuples_inserted and tuples_deleted up to the
+ * or abort, we propagate tuples_inserted/updated/deleted up to the
  * parent subtransaction level, or out to the parent PgStat_TableStatus,
  * as appropriate.
  * ----------
@@ -129,6 +130,7 @@ typedef struct PgStat_TableStatus
 typedef struct PgStat_TableXactStatus
 {
    PgStat_Counter tuples_inserted;     /* tuples inserted in (sub)xact */
+   PgStat_Counter tuples_updated;      /* tuples updated in (sub)xact */
    PgStat_Counter tuples_deleted;      /* tuples deleted in (sub)xact */
    int         nest_level;     /* subtransaction nest level */
    /* links to other structs for same relation: */
@@ -274,7 +276,7 @@ typedef struct PgStat_MsgAutovacStart
 
 /* ----------
  * PgStat_MsgVacuum                Sent by the backend or autovacuum daemon
- *                             after VACUUM or VACUUM ANALYZE
+ *                             after VACUUM
  * ----------
  */
 typedef struct PgStat_MsgVacuum
@@ -282,9 +284,8 @@ typedef struct PgStat_MsgVacuum
    PgStat_MsgHdr m_hdr;
    Oid         m_databaseid;
    Oid         m_tableoid;
-   bool        m_analyze;
+   bool        m_adopt_counts;
    bool        m_autovacuum;
-   bool        m_scanned_all;
    TimestampTz m_vacuumtime;
    PgStat_Counter m_tuples;
 } PgStat_MsgVacuum;
@@ -300,6 +301,7 @@ typedef struct PgStat_MsgAnalyze
    PgStat_MsgHdr m_hdr;
    Oid         m_databaseid;
    Oid         m_tableoid;
+   bool        m_adopt_counts;
    bool        m_autovacuum;
    TimestampTz m_analyzetime;
    PgStat_Counter m_live_tuples;
@@ -478,7 +480,7 @@ typedef struct PgStat_StatTabEntry
 
    PgStat_Counter n_live_tuples;
    PgStat_Counter n_dead_tuples;
-   PgStat_Counter last_anl_tuples;
+   PgStat_Counter changes_since_analyze;
 
    PgStat_Counter blocks_fetched;
    PgStat_Counter blocks_hit;
@@ -635,11 +637,10 @@ extern void pgstat_clear_snapshot(void);
 extern void pgstat_reset_counters(void);
 
 extern void pgstat_report_autovac(Oid dboid);
-extern void pgstat_report_vacuum(Oid tableoid, bool shared, bool scanned_all,
-                    bool analyze, PgStat_Counter tuples);
-extern void pgstat_report_analyze(Relation rel,
-                     PgStat_Counter livetuples,
-                     PgStat_Counter deadtuples);
+extern void pgstat_report_vacuum(Oid tableoid, bool shared, bool adopt_counts,
+                    PgStat_Counter tuples);
+extern void pgstat_report_analyze(Relation rel, bool adopt_counts,
+                     PgStat_Counter livetuples, PgStat_Counter deadtuples);
 
 extern void pgstat_initialize(void);
 extern void pgstat_bestart(void);