Initial revision
authorteodor <teodor>
Thu, 29 Sep 2011 12:26:25 +0000 (12:26 +0000)
committerteodor <teodor>
Thu, 29 Sep 2011 12:26:25 +0000 (12:26 +0000)
Makefile [new file with mode: 0644]
README.online_analyze [new file with mode: 0644]
online_analyze.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..333add2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+MODULE_big = online_analyze
+OBJS = online_analyze.o     
+#DATA_built = online_analyze.sql
+DOCS = README.online_analyze
+#REGRESS = online_analyze
+
+ifdef USE_PGXS
+PGXS := $(shell pg_config --pgxs)
+include $(PGXS)
+else
+subdir = contrib/online_analyze
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
diff --git a/README.online_analyze b/README.online_analyze
new file mode 100644 (file)
index 0000000..edf5059
--- /dev/null
@@ -0,0 +1,26 @@
+Module makes an analyze call immediately after INSERT/UPDATE/DELETE/SELECT INTO
+for affected table(s).
+
+Supported versionsi of PostgreSQL: 8.4.*, 9.0.*, 9.1.*
+
+Usage: LOAD 'online_analyze';
+
+Custom variables (defaults values are shown):
+online_analyze.enable = on  
+       Enables on-line analyze
+
+online_analyze.verbose = on
+       Execute ANALYZE VERBOSE
+
+online_analyze.scale_factor = 0.1
+       Fraction of table size to start on-line analyze (similar to
+       autovacuum_analyze_scale_factor)
+
+online_analyze.threshold = 50
+       Min number of row updates before on-line analyze (similar to
+       autovacuum_analyze_threshold)
+
+online_analyze.min_interval = 10000
+    Minimum time interval between analyze call per table (in milliseconds)
+
+Author: Teodor Sigaev <teodor@sigaev.ru>
diff --git a/online_analyze.c b/online_analyze.c
new file mode 100644 (file)
index 0000000..d8010af
--- /dev/null
@@ -0,0 +1,272 @@
+#include "postgres.h"
+
+#include "pgstat.h"
+#include "catalog/namespace.h"
+#include "commands/vacuum.h"
+#include "executor/executor.h"
+#include "nodes/nodes.h"
+#include "nodes/parsenodes.h"
+#include "storage/bufmgr.h"
+#include "utils/lsyscache.h"
+#include "utils/guc.h"
+
+#ifdef PG_MODULE_MAGIC
+PG_MODULE_MAGIC;
+#endif
+
+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 double online_analyze_min_interval = 10000;
+
+static ExecutorEnd_hook_type oldhook = NULL;
+
+static void
+makeAnalyze(Oid relOid, CmdType operation, uint32 naffected)
+{
+       PgStat_StatTabEntry             *tabentry;
+       TimestampTz                     now = GetCurrentTimestamp();
+
+       if (relOid == InvalidOid)
+               return;
+
+       tabentry = pgstat_fetch_stat_tabentry(relOid);
+
+#if PG_VERSION_NUM >= 90000
+#define changes_since_analyze(t)       ((t)->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
+               )
+       )
+       {
+               VacuumStmt                              vacstmt;
+               TimestampTz                             startStamp, endStamp;
+
+               vacstmt.type = T_VacuumStmt;
+               vacstmt.freeze_min_age = -1;
+               vacstmt.freeze_table_age = -1; /* ??? */
+               vacstmt.relation = NULL;
+               vacstmt.va_cols = NIL;
+
+#if PG_VERSION_NUM >= 90000
+               vacstmt.options = VACOPT_ANALYZE;
+               if (online_analyze_verbose)
+                       vacstmt.options |= VACOPT_VERBOSE;
+#else
+               vacstmt.vacuum = vacstmt.full = false;
+               vacstmt.analyze = true;
+               vacstmt.verbose = online_analyze_verbose;
+#endif
+
+               if (online_analyze_verbose)
+                       startStamp = GetCurrentTimestamp();
+
+               analyze_rel(relOid, &vacstmt, GetAccessStrategy(BAS_VACUUM)
+#if (PG_VERSION_NUM < 90004) && (PG_VERSION_NUM >= 90000)
+                       , true
+#endif
+               );
+
+               if (online_analyze_verbose)
+               {
+                       long    secs;
+                       int             microsecs;
+
+                       endStamp = GetCurrentTimestamp();
+                       TimestampDifference(startStamp, endStamp, &secs, &microsecs);
+                       elog(INFO, "analyze \"%s\" took %.02f seconds", get_rel_name(relOid), ((double)secs) + ((double)microsecs)/1.0e6);
+               }
+
+
+               if (tabentry == NULL)
+               {
+                       /* new table */
+                       pgstat_clear_snapshot();
+               }
+               else
+               {
+                       /* update last analyze timestamp in local memory of backend */
+                       tabentry->analyze_timestamp = now;
+               }
+       }
+#if PG_VERSION_NUM >= 90000
+       else if (tabentry != NULL)
+       {
+               tabentry->changes_since_analyze += naffected;
+       }
+#endif
+}
+
+extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc);
+void
+onlineAnalyzeHooker(QueryDesc *queryDesc) 
+{
+       uint32  naffected = 0;
+
+       if (queryDesc->estate)
+               naffected = queryDesc->estate->es_processed;    
+
+       if (online_analyze_enable && queryDesc->plannedstmt &&
+                       (queryDesc->operation == CMD_INSERT || 
+                        queryDesc->operation == CMD_UPDATE ||
+                        queryDesc->operation == CMD_DELETE ||
+                        (queryDesc->operation == CMD_SELECT && queryDesc->plannedstmt->intoClause)))
+       {
+               if (queryDesc->plannedstmt->intoClause)
+               {
+                       Oid     relOid = RangeVarGetRelid(queryDesc->plannedstmt->intoClause->rel, true);
+
+                       makeAnalyze(relOid, queryDesc->operation, naffected);
+               }
+               else if (queryDesc->plannedstmt->resultRelations &&
+                                queryDesc->plannedstmt->rtable)
+               {
+                       ListCell        *l;
+
+                       foreach(l, queryDesc->plannedstmt->resultRelations)
+                       {
+                               int                     n = lfirst_int(l);
+                               RangeTblEntry   *rte = list_nth(queryDesc->plannedstmt->rtable, n-1);
+               
+                               if (rte->rtekind == RTE_RELATION)
+                                       makeAnalyze(rte->relid, queryDesc->operation, naffected);
+                       }
+               }
+       }
+
+       if (oldhook)
+               (*oldhook)(queryDesc);
+       else
+               standard_ExecutorEnd(queryDesc);
+}
+
+void _PG_init(void);
+void
+_PG_init(void)
+{
+       oldhook = ExecutorEnd_hook;
+
+       ExecutorEnd_hook = onlineAnalyzeHooker;
+
+       DefineCustomBoolVariable(
+               "online_analyze.enable",
+               "Enable on-line analyze",
+               "Enables analyze of table directly after insert/update/delete/select into",
+               &online_analyze_enable,
+#if PG_VERSION_NUM >= 80400
+               online_analyze_enable,
+#endif
+               PGC_USERSET,
+#if PG_VERSION_NUM >= 80400
+               GUC_NOT_IN_SAMPLE,
+#if PG_VERSION_NUM >= 90100
+               NULL,
+#endif
+#endif
+               NULL,
+               NULL
+       );
+
+       DefineCustomBoolVariable(
+               "online_analyze.verbose",
+               "Verbosity of on-line analyze",
+               "Make ANALYZE VERBOSE after table's changes",
+               &online_analyze_verbose,
+#if PG_VERSION_NUM >= 80400
+               online_analyze_verbose,
+#endif
+               PGC_USERSET,
+#if PG_VERSION_NUM >= 80400
+               GUC_NOT_IN_SAMPLE,
+#if PG_VERSION_NUM >= 90100
+               NULL,
+#endif
+#endif
+               NULL,
+               NULL
+       );
+
+    DefineCustomRealVariable(
+               "online_analyze.scale_factor",
+               "fraction of table size to start on-line analyze",
+               "fraction of table size to start on-line analyze",
+               &online_analyze_scale_factor,
+#if PG_VERSION_NUM >= 80400
+               online_analyze_scale_factor,
+#endif
+               0.0,
+               1.0,
+               PGC_USERSET,
+#if PG_VERSION_NUM >= 80400
+               GUC_NOT_IN_SAMPLE,
+#if PG_VERSION_NUM >= 90100
+               NULL,
+#endif
+#endif
+               NULL,
+               NULL
+       );
+
+    DefineCustomIntVariable(
+               "online_analyze.threshold",
+               "min number of row updates before on-line analyze",
+               "min number of row updates before on-line analyze",
+               &online_analyze_threshold,
+#if PG_VERSION_NUM >= 80400
+               online_analyze_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)",
+               "minimum time interval between analyze call (in milliseconds)",
+               &online_analyze_scale_factor,
+#if PG_VERSION_NUM >= 80400
+               online_analyze_min_interval,
+#endif
+               0.0,
+               1e30,
+               PGC_USERSET,
+#if PG_VERSION_NUM >= 80400
+               GUC_NOT_IN_SAMPLE,
+#if PG_VERSION_NUM >= 90100
+               NULL,
+#endif
+#endif
+               NULL,
+               NULL
+       );
+
+}
+
+void _PG_fini(void);
+void
+_PG_fini(void)
+{
+       ExecutorEnd_hook = oldhook;
+}