fix compile with pg < 9.6, fix memory context allocation
[online_analyze.git] / online_analyze.c
index 0d85df6..b1a3578 100644 (file)
@@ -37,6 +37,8 @@
 #include "nodes/parsenodes.h"
 #include "storage/bufmgr.h"
 #include "utils/builtins.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
 #include "utils/lsyscache.h"
 #include "utils/guc.h"
 #if PG_VERSION_NUM >= 90200
@@ -59,6 +61,7 @@ static bool online_analyze_enable = true;
 static bool online_analyze_verbose = true;
 static double online_analyze_scale_factor = 0.1;
 static int online_analyze_threshold = 50;
+static int online_analyze_capacity_threshold = 100000;
 static double online_analyze_min_interval = 10000;
 
 static ExecutorEnd_hook_type oldExecutorEndHook = NULL;
@@ -94,6 +97,20 @@ typedef struct TableList {
 static TableList excludeTables = {0, NULL, NULL};
 static TableList includeTables = {0, NULL, NULL};
 
+typedef struct OnlineAnalyzeTableStat {
+       Oid                             tableid;
+       bool                    rereadStat;
+       PgStat_Counter  n_tuples;
+       PgStat_Counter  changes_since_analyze;
+       TimestampTz             autovac_analyze_timestamp;
+       TimestampTz             analyze_timestamp;
+} OnlineAnalyzeTableStat;
+
+static MemoryContext   onlineAnalyzeMemoryContext = NULL;
+static HTAB    *relstats = NULL;
+
+static void relstatsInit(void);
+
 static int
 oid_cmp(const void *a, const void *b)
 {
@@ -321,19 +338,42 @@ makeRangeVarFromOid(Oid relOid)
 static void
 makeAnalyze(Oid relOid, CmdType operation, int32 naffected)
 {
-       PgStat_StatTabEntry             *tabentry;
        TimestampTz                             now = GetCurrentTimestamp();
+       Relation                                rel;
+       OnlineAnalyzeTableType  reltype;
+       bool                                    found = false,
+                                                       newTable = false;
+       OnlineAnalyzeTableStat  *rstat,
+                                                       dummyrstat;
+       PgStat_StatTabEntry             *tabentry = NULL;
 
        if (relOid == InvalidOid)
                return;
 
        if (naffected == 0)
-               /* return if there is not changes */
+               /* return if there is no changes */
                return;
        else if (naffected < 0)
                /* number if affected rows is unknown */
                naffected = 0;
 
+       rel = RelationIdGetRelation(relOid);
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+       {
+               RelationClose(rel);
+               return;
+       }
+
+       reltype =
+#if PG_VERSION_NUM >= 90100
+               (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+#else
+               (rel->rd_istemp || rel->rd_islocaltemp)
+#endif
+                       ? OATT_TEMPORARY : OATT_PERSISTENT;
+
+       RelationClose(rel);
+
        /*
         * includeTables overwrites excludeTables
         */
@@ -353,61 +393,71 @@ makeAnalyze(Oid relOid, CmdType operation, int32 naffected)
                case OATT_TEMPORARY:
                case OATT_PERSISTENT:
                default:
+                       /*
+                        * skip analyze if relation's type doesn't not match
+                        * online_analyze_table_type
+                        */
+                       if ((online_analyze_table_type & reltype) == 0 ||
+                               matchOid(&excludeTables, relOid) == true)
                        {
-                               Relation                                rel;
-                               OnlineAnalyzeTableType  reltype;
-
-                               rel = RelationIdGetRelation(relOid);
-
-                               if (rel->rd_rel->relkind != RELKIND_RELATION)
-                               {
-                                       RelationClose(rel);
+                               if (matchOid(&includeTables, relOid) == false)
                                        return;
-                               }
-
-                               reltype =
-#if PG_VERSION_NUM >= 90100
-                                       (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
-#else
-                                       (rel->rd_istemp || rel->rd_islocaltemp)
-#endif
-                                               ? OATT_TEMPORARY : OATT_PERSISTENT;
-                               RelationClose(rel);
-
-                               /*
-                                * skip analyze if relation's type doesn't not match
-                                * online_analyze_table_type
-                                */
-                               if ((online_analyze_table_type & reltype) == 0 ||
-                                       matchOid(&excludeTables, relOid) == true)
-                               {
-                                       if (matchOid(&includeTables, relOid) == false)
-                                               return;
-                               }
-                               break;
                        }
+                       break;
        }
 
-       tabentry = pgstat_fetch_stat_tabentry(relOid);
+       /*
+        * Do not store data about persistent table in local memory because we
+        * could not track changes of them: they could be changed by another
+        * backends. So always get a pgstat table entry.
+        */
+       if (reltype == OATT_TEMPORARY)
+               rstat = hash_search(relstats, &relOid, HASH_ENTER, &found);
+       else
+               rstat = &dummyrstat; /* found == false for following if */
+
+       if (found == false || rstat->rereadStat == true || naffected == 0)
+       {
+
+               if (!found)
+               {
+                       MemSet(rstat, 0, sizeof(*rstat));
+                       rstat->tableid = relOid;
+               }
+               Assert(rstat->tableid == relOid);
+
+               tabentry = pgstat_fetch_stat_tabentry(relOid);
 
+               if (tabentry)
+               {
+                       rstat->n_tuples = tabentry->n_dead_tuples + tabentry->n_live_tuples;
+                       rstat->changes_since_analyze =
 #if PG_VERSION_NUM >= 90000
-#define changes_since_analyze(t)       ((t)->changes_since_analyze)
+                               tabentry->changes_since_analyze;
 #else
-#define changes_since_analyze(t)       ((t)->n_live_tuples + (t)->n_dead_tuples - (t)->last_anl_tuples)
-#endif
-
-       if (
-               tabentry == NULL /* a new table */ ||
-               (
-                       /* do not analyze too often, if both stamps are exceeded the go */
-                       TimestampDifferenceExceeds(tabentry->analyze_timestamp, now, online_analyze_min_interval) &&
-                       TimestampDifferenceExceeds(tabentry->autovac_analyze_timestamp, now, online_analyze_min_interval) &&
-                       /* be in sync with relation_needs_vacanalyze */
-                       ((double)(changes_since_analyze(tabentry) + naffected)) >=
-                               online_analyze_scale_factor * ((double)(tabentry->n_dead_tuples + tabentry->n_live_tuples)) +
-                                       (double)online_analyze_threshold
-               )
-       )
+                               tabentry->n_live_tuples + tabentry->n_dead_tuples -
+                                       tabentry->last_anl_tuples;
+#endif
+                       rstat->autovac_analyze_timestamp =
+                               tabentry->autovac_analyze_timestamp;
+                       rstat->analyze_timestamp = tabentry->analyze_timestamp;
+                       rstat->rereadStat = false;
+               }
+               else
+               {
+                       newTable = true;
+                       rstat->rereadStat = true;
+               }
+       }
+
+       if (newTable || (
+               /* do not analyze too often, if both stamps are exceeded the go */
+               TimestampDifferenceExceeds(rstat->analyze_timestamp, now, online_analyze_min_interval) &&
+               TimestampDifferenceExceeds(rstat->autovac_analyze_timestamp, now, online_analyze_min_interval) &&
+               /* be in sync with relation_needs_vacanalyze */
+               ((double)(rstat->changes_since_analyze + naffected)) >=
+                        online_analyze_scale_factor * ((double)rstat->n_tuples) +
+                        (double)online_analyze_threshold))
        {
 #if PG_VERSION_NUM < 90500
                VacuumStmt                              vacstmt;
@@ -416,6 +466,7 @@ makeAnalyze(Oid relOid, CmdType operation, int32 naffected)
 #endif
                TimestampTz                             startStamp, endStamp;
 
+
                memset(&startStamp, 0, sizeof(startStamp)); /* keep compiler quiet */
 
                memset(&vacstmt, 0, sizeof(vacstmt));
@@ -474,23 +525,34 @@ makeAnalyze(Oid relOid, CmdType operation, int32 naffected)
                                ((double)secs) + ((double)microsecs)/1.0e6);
                }
 
-               if (tabentry == NULL)
-               {
-                       /* new table */
-                       pgstat_clear_snapshot();
-               }
-               else
+               rstat->autovac_analyze_timestamp = now;
+               rstat->changes_since_analyze = 0;
+               rstat->rereadStat = true;
+
+               /* update last analyze timestamp in local memory of backend */
+               if (tabentry)
                {
-                       /* update last analyze timestamp in local memory of backend */
                        tabentry->analyze_timestamp = now;
+                       tabentry->changes_since_analyze = 0;
                }
+#if 0
+               /* force reload stat for new table */
+               if (newTable)
+                       pgstat_clear_snapshot();
+#endif
        }
-#if PG_VERSION_NUM >= 90000
-       else if (tabentry != NULL)
+       else
        {
-               tabentry->changes_since_analyze += naffected;
-       }
+#if PG_VERSION_NUM >= 90000
+               if (tabentry)
+                       tabentry->changes_since_analyze += naffected;
 #endif
+               rstat->changes_since_analyze += naffected;
+       }
+
+       /* Reset local cache if we are over limit */
+       if (hash_get_num_entries(relstats) > online_analyze_capacity_threshold)
+               relstatsInit();
 }
 
 extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc);
@@ -582,10 +644,55 @@ onlineAnalyzeHookerUtility(Node *parsetree, const char *queryString,
 }
 #endif
 
+static void
+relstatsInit(void)
+{
+       HASHCTL hash_ctl;
+       int             flags = 0;
+
+       MemSet(&hash_ctl, 0, sizeof(hash_ctl));
+
+       hash_ctl.hash = oid_hash;
+       flags |= HASH_FUNCTION;
+
+       if (onlineAnalyzeMemoryContext)
+       {
+               Assert(relstats != NULL);
+               MemoryContextReset(onlineAnalyzeMemoryContext);
+       }
+       else
+       {
+               Assert(relstats == NULL);
+               onlineAnalyzeMemoryContext =
+                       AllocSetContextCreate(CacheMemoryContext,
+                                                                 "online_analyze storage context",
+#if PG_VERSION_NUM < 90600
+                                                                 ALLOCSET_DEFAULT_MINSIZE,
+                                                                 ALLOCSET_DEFAULT_INITSIZE,
+                                                                 ALLOCSET_DEFAULT_MAXSIZE
+#else
+                                                                 ALLOCSET_DEFAULT_SIZES
+#endif
+                                                                );
+       }
+
+       hash_ctl.hcxt = onlineAnalyzeMemoryContext;
+       flags |= HASH_CONTEXT;
+
+       hash_ctl.keysize = sizeof(Oid);
+
+       hash_ctl.entrysize = sizeof(OnlineAnalyzeTableStat);
+       flags |= HASH_ELEM;
+
+       relstats = hash_create("online_analyze storage", 1024, &hash_ctl, flags);
+}
+
 void _PG_init(void);
 void
 _PG_init(void)
 {
+       relstatsInit();
+
        oldExecutorEndHook = ExecutorEnd_hook;
 
        ExecutorEnd_hook = onlineAnalyzeHooker;
@@ -677,6 +784,27 @@ _PG_init(void)
                NULL
        );
 
+       DefineCustomIntVariable(
+               "online_analyze.capacity_threshold",
+               "Max local cache table capacity",
+               "Max local cache table capacity",
+               &online_analyze_capacity_threshold,
+#if PG_VERSION_NUM >= 80400
+               online_analyze_capacity_threshold,
+#endif
+               0,
+               0x7fffffff,
+               PGC_USERSET,
+#if PG_VERSION_NUM >= 80400
+               GUC_NOT_IN_SAMPLE,
+#if PG_VERSION_NUM >= 90100
+               NULL,
+#endif
+#endif
+               NULL,
+               NULL
+       );
+
        DefineCustomRealVariable(
                "online_analyze.min_interval",
                "minimum time interval between analyze call (in milliseconds)",