未验证 提交 0b17968e 编写于 作者: 盏一 提交者: GitHub

Enable autoanalyze in Greenplum (#10515)

The basic idea for only enabling auto-ANALYZE through Master’s autovacuum
daemon is to collect pgstat info into Master when executing queries. Start
the Master’s autovacuum launcher process. Fire an autovacuum work process for
a database on Master when the naptime reaches. Then the autovacuum worker
will iterate through all tables/materialized views under a specified database,
and execute ANALYZE for tables which reached the analyze threshold. Note the
ANALYZE statement issued in the autovacuum worker on Master is the same as
executing it through query on QD. ie. The auto-ANALYZE is coordinated by the
master and segments do not start it’s own autovacuum launcher and autovacuum
worker.

More details please refer to src/backend/postmaster/README.auto-ANALYZE.
Co-authored-by: NJunfeng(Jerome) Yang <jeyang@pivotal.io>
上级 89a11719
......@@ -132,6 +132,7 @@
#include "funcapi.h"
#include "libpq-fe.h"
#include "utils/builtins.h"
#include "utils/faultinjector.h"
#include "utils/hyperloglog/gp_hyperloglog.h"
#include "utils/snapmgr.h"
......@@ -277,6 +278,12 @@ analyze_rel_internal(Oid relid, RangeVar *relation, int options,
if (!onerel)
return;
#ifdef FAULT_INJECTOR
FaultInjector_InjectFaultIfSet(
"analyze_after_hold_lock", DDLNotSpecified,
"", RelationGetRelationName(onerel));
#endif
/*
* Check permissions --- this should match vacuum's check!
*/
......
......@@ -75,6 +75,7 @@
#include "utils/metrics_utils.h"
#include "utils/resscheduler.h"
#include "utils/string_utils.h"
#include "pgstat.h"
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
......@@ -1273,7 +1274,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
/* Issue automatic ANALYZE if conditions are satisfied (MPP-4082). */
if (Gp_role == GP_ROLE_DISPATCH && is_from)
auto_stats(AUTOSTATS_CMDTYPE_COPY, relid, *processed, false /* inFunction */);
collect_tabstat(AUTOSTATS_CMDTYPE_COPY, relid, *processed, false /* inFunction */);
return relid;
}
......
......@@ -62,6 +62,7 @@
#include "cdb/cdbutil.h"
#include "cdb/cdbvars.h"
#include "cdb/memquota.h"
#include "pgstat.h"
typedef struct
{
......@@ -492,7 +493,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
/* MPP-14001: Running auto_stats */
if (Gp_role == GP_ROLE_DISPATCH)
auto_stats(cmdType, relationOid, queryDesc->es_processed, false /* inFunction */);
collect_tabstat(cmdType, relationOid, queryDesc->es_processed, false /* inFunction */);
}
{
......
......@@ -15362,7 +15362,7 @@ ATExecExpandTableCTAS(AlterTableCmd *rootCmd, Relation rel, AlterTableCmd *cmd)
ExecutorFinish(queryDesc);
ExecutorEnd(queryDesc);
auto_stats(cmdType, relationOid, queryDesc->es_processed, false);
collect_tabstat(cmdType, relationOid, queryDesc->es_processed, false);
FreeQueryDesc(queryDesc);
......@@ -15874,7 +15874,7 @@ ATExecSetDistributedBy(Relation rel, Node *node, AlterTableCmd *cmd)
ExecutorEnd(queryDesc);
if (Gp_role == GP_ROLE_DISPATCH)
auto_stats(cmdType,
collect_tabstat(cmdType,
relationOid,
queryDesc->es_processed,
false);
......
......@@ -419,6 +419,15 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
analyze_rel(relid, relation, options, params,
va_cols, in_outer_xact, vac_strategy, NULL);
#ifdef FAULT_INJECTOR
if (IsAutoVacuumWorkerProcess())
{
FaultInjector_InjectFaultIfSet(
"analyze_finished_one_relation", DDLNotSpecified,
"", relation->relname);
}
#endif
if (use_own_xacts)
{
PopActiveSnapshot();
......
......@@ -44,6 +44,7 @@
#include "executor/spi.h"
#include "cdb/memquota.h"
#include "postmaster/autostats.h"
#include "pgstat.h"
/*
......@@ -1025,7 +1026,7 @@ postquel_end(execution_state *es)
/* MPP-14001: Running auto_stats */
if (Gp_role == GP_ROLE_DISPATCH)
auto_stats(cmdType, relationOid, es->qd->es_processed, true /* inFunction */);
collect_tabstat(cmdType, relationOid, es->qd->es_processed, true /* inFunction */);
}
(*es->qd->dest->rDestroy) (es->qd->dest);
......
......@@ -47,6 +47,7 @@
#include "executor/functions.h"
#include "cdb/memquota.h"
#include "parser/analyze.h"
#include "pgstat.h"
uint64 SPI_processed = 0;
......@@ -2665,7 +2666,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
/* MPP-14001: Running auto_stats */
if (Gp_role == GP_ROLE_DISPATCH)
auto_stats(cmdType, relationOid, queryDesc->es_processed, true /* inFunction */);
collect_tabstat(cmdType, relationOid, queryDesc->es_processed, true /* inFunction */);
}
PG_CATCH();
{
......
Enable auto-ANALYZE through autovacuum daemon on Master(QD) in GPDB.
The basic idea for only enabling auto-ANALYZE through Master’s autovacuum
daemon is to collect pgstat info into Master when executing queries. Start
the Master’s autovacuum launcher process. Fire an autovacuum work process for
a database on Master when the naptime reaches. Then the autovacuum worker
will iterate through all tables/materialized views under a specified database,
and execute ANALYZE for tables which reached the analyze threshold. Note the
ANALYZE statement issued in the autovacuum worker on Master is the same as
executing it through query on QD. ie. The auto-ANALYZE is coordinated by the
master and segments do not start it’s own autovacuum launcher and autovacuum
worker.
Implementations
=================================================
### Collect pgstat on master for relations.
Since the auto-ANALYZE is fired from Master, we need to collect pgstat info
into master. GPDB used to call `auto_stats` for queries that may process tuples
for different command types like INSERT/UPDATE/COPY/… to execute ANALYZE based
on the `auto_stats` configuration(it’ll add overhead for a simple command,
more details please refer to the discussion
https://groups.google.com/a/greenplum.org/g/gpdb-dev/c/gqordopb6Gg).
The normal pgstat logic(from Postgres upstream) only updates table stats in the
table modification level(like `heap_insert`, `heap_update`, `heap_delete` ...).
On Master, since there’s no data existing for these distributed tables, the
pgstat will never get updated before.
Now we bring in another function `gp_pgstat_report_tabstat` which should be
called at the end of command execution when tuples are processed like
`auto_stats`.
GPDB collects processed tuples from all segments, and passes the count to
report pgstat for the processed table with the current command on Master.
`gp_pgstat_report_tabstat` will choose to report INSERT/UPDATE/DELETE based
on command type.
We wrap the `auto_stats` and `gp_pgstat_report_tabstat` in a function called
`collect_tabstat`. And put it as where `auto_stats` used to be.
In the `collect_tabstat`, when `autovacuum` is enabled on Master (now for
ANALYZE only). It’ll call `gp_pgstat_report_tabstat` to collect pgstat to
support auto-ANALYZE. Currently this is not supported for the partition table,
because we don't have accurate pgstat on QD for parent tables and child tables.
So still execute `auto_stats` for the partition table. For relation who's
storage parameter `autovacuum_enable=false` with autovacuum enabled, neither
autovacuum ANALYZE nor `auto_stat`s will execute for it. Users need to manually
run ANALYZE for it. This is the same with Postgres. If the `autovacuum` is not
enabled on Master, `auto_stats` will be executed. `collect_tabstat` only gets
called on QD.
### Make the autovacuum guc work to start the autovacuum launcher.
We used to disable modifying autovacuum ANALYZE related GUCs. Now, only enable
`autovacuum`, `autovacuum_naptime`, `autovacuum_analyze_threshold`,
`autovacuum_analyze_scale_factor`.
### Recognize databases that need vacuum(template0 only) or analyze under the autovacuum launcher.
As in PR https://github.com/greenplum-db/gpdb/pull/4548. We support force xid
wraparound VACUUM through autovacuum for template0 only. This is by checking
the database’s `datallowconn` field. If it’s false, it means not allowing users
to connect it directly. That’s also the reason why we enable force xid wraparound
VACUUM for template0. In `get_database_list` under autovacuum.c, it used to only
return the databases whos `datallowconn=false`. Note the force xid wraparound
VACUUM for template0 could be run on both **Master** and **segments**.
Then we reuse the `datallowconn` for auto-ANALYZE logic. If the current
database’s `datallowconn` sets to true, we only perform ANALYZE in autovacuum
workers on Master. Now in `get_database_list`, we check if the
`datallowconn=true`, autovacuum is enabled and current autovacuum daemon is
Master’s, we perverse the `datallowconn` in the returned database list. The new
logic limit only the **Master’s** autovacuum daemon has the ability to fire
autovacuum workers on databases other than template0( so if enable autovacuum
on segments, it still only executes force xid wraparound on template0).
Also a new field `wi_for_analyze` in `WorkerInfoData` is added to pass this
into the autovacuum worker. Then if the target database’s `datallowconn=true`
for a new autovacuum worker, the worker’s `wi_for_analyze` will be set to true,
and the started worker will only execute ANALYZE logic. Other tables xid
wraparound VACUUM are ignored.
We also optimize the logic of `do_start_worker`, it used to always choose the
force xid wraparound database as the target for a new autovacuum worker. Since
we only enable auto-ANALYZE for databases except template0, the old logic will
be trapped into a database who needs to execute xid wraparound. But autovacuum
workers never execute VACUUM on it.
### Change autovacuum worker’s Gp_role for ANALYZE.
Since auto-ANALYZE is coordinated by the Master and segments do not start it’s
own autovacuum launcher and autovacuum worker. We need Master’s autovacuum
workers which do auto-ANALYZE on a database have `Gp_role = GP_ROLE_DISPATCH`.
So it’ll do the ANALYZE on master to calculate statistics with right behavior,
for example, acquire sample rows from segments.
But for VACUUM on template0, we still keep it running as `GP_ROLE_UTILITY`.
### Autovacuum workers for ANALYZE should not block other commands.
In postgres, the autovacuum workers generally don't block other commands.
If a process attempts to acquire a lock that conflicts with the SHARE UPDATE
EXCLUSIVE lock held by autovacuum, lock acquisition will interrupt the
autovacuum.
This feature should also work for the auto-ANALYZE’s autovacuum workers.
A test is added to ensure this(src/test/isolation2/input/autovacuum-analyze.source).
A simple example is:
```sql
CREATE TABLE anaabort(id int);
INSERT INTO anaabort select i from generate_series(1, 1000) as i;
-- assume the autovacuum worker is executing ANALYZE on the table
T1: BEGIN;
T1: LOCK TABLE anaabort in EXCLUSIVE mode; -- abort the autovacuum worker
T1: ...
T1: END;
-- Then the autovacuum worker's ANALYZE command should abort and the
-- pg_statistic should not get updated.
SELECT count(*) FROM pg_statistic where starelid = 'anaabort'::regclass;
count
-------
0
(1 row)
```
Limitations
==================================================
### Partition table support
Current implementation does not collect pgstat for partition tables. So the
auto-ANALYZE will not execute for them.
The reason is we can not collect accurate pgstat for the partition table.
For examples:
- Insert into the child table, it updates the pgstat for the child table, but
the root table's pgstat does not get updated on master. (Although we can
simply add it to the root table)
- Insert into the root table, it updates the pgstat for the root table on master,
but underly child tables' pgstat not get updated, since GPDB only gets
processed tuple count on master at each command execution end phase where
`collect_tabstat` gets called. We need to collect tuple count for each child
table on segments into master.
Once we did the PG12 merge, we could try to resolve it.
And for Postgres, it only collects pgstat for child tables, this makes
auto-ANALYZE only executed for child tables. User still needs to manually
ANALYZE on the parent table. See related discussions in pg-hackers:
- https://www.postgresql.org/message-id/2674.1262040064@sss.pgh.pa.us
- https://www.postgresql.org/message-id/CAM-w4HMQKC8hw7nB9TW3OV%2BhkB5OUcPtvr_U_EiSOjByoa-e4Q%40mail.gmail.com
### Cost-based delay
In Postgres, the cost-base delay feature is used to limit the VACUUM and
ANALYZE IO cost. Since the IO cost is not collected to master, so feature
is not working in cluster wide. But we can still use it to limit IO cost for
each segment.
We plan to support the disk io limit in the resource group. Once it is ready,
we will see if the resource group can help here.
### VACUUM under autovacuum
Now, GPDB only supports xid wrap around VACUUM on template0 through the
autovacuum. Since we still have different opinions for the auto-vacuum
implementation, current PR does not include the vacuum part.
......@@ -106,6 +106,7 @@
#include "utils/tqual.h"
#include "cdb/cdbvars.h"
#include "cdb/cdbpartition.h"
#include "utils/faultinjector.h"
......@@ -170,6 +171,7 @@ typedef struct avl_dbase
typedef struct avw_dbase
{
Oid adw_datid;
bool adw_allowconn;
char *adw_name;
TransactionId adw_frozenxid;
MultiXactId adw_minmulti;
......@@ -210,6 +212,8 @@ typedef struct autovac_table
* wi_dboid OID of the database this worker is supposed to work on
* wi_tableoid OID of the table currently being vacuumed, if any
* wi_sharedrel flag indicating whether table is marked relisshared
* wi_for_analyze true means that we should do only analyze for wi_dboid.
* false means that we should do only vacuum for wi_dboid.
* wi_proc pointer to PGPROC of the running worker, NULL if not started
* wi_launchtime Time at which this worker was launched
* wi_cost_* Vacuum cost-based delay parameters current in this worker
......@@ -228,6 +232,7 @@ typedef struct WorkerInfoData
TimestampTz wi_launchtime;
bool wi_dobalance;
bool wi_sharedrel;
bool wi_for_analyze; /* GPDB only */
int wi_cost_delay;
int wi_cost_limit;
int wi_cost_limit_base;
......@@ -1194,10 +1199,17 @@ do_start_worker(void)
avw_dbase *tmp = lfirst(cell);
dlist_iter iter;
/* Check to see if this one is at risk of wraparound */
if (TransactionIdPrecedes(tmp->adw_frozenxid, xidForceLimit))
/* Check to see if this one is at risk of wraparound
*
* GPDB enable auto-analyze on master, and currently only template0
* need to deal with the xid wrap around. So ignore other dbs for
* the wraparound check.
*/
if (!tmp->adw_allowconn &&
TransactionIdPrecedes(tmp->adw_frozenxid, xidForceLimit))
{
if (avdb == NULL ||
avdb->adw_allowconn || /* GPDB: only do anti-wraparound for !datallowconn databases */
TransactionIdPrecedes(tmp->adw_frozenxid,
avdb->adw_frozenxid))
avdb = tmp;
......@@ -1206,9 +1218,11 @@ do_start_worker(void)
}
else if (for_xid_wrap)
continue; /* ignore not-at-risk DBs */
else if (MultiXactIdPrecedes(tmp->adw_minmulti, multiForceLimit))
else if (!tmp->adw_allowconn &&
MultiXactIdPrecedes(tmp->adw_minmulti, multiForceLimit))
{
if (avdb == NULL ||
avdb->adw_allowconn || /* GPDB: only do anti-wraparound for !datallowconn databases */
MultiXactIdPrecedes(tmp->adw_minmulti, avdb->adw_minmulti))
avdb = tmp;
for_multi_wrap = true;
......@@ -1284,6 +1298,7 @@ do_start_worker(void)
worker = dlist_container(WorkerInfoData, wi_links, wptr);
worker->wi_dboid = avdb->adw_datid;
worker->wi_for_analyze = avdb->adw_allowconn;
worker->wi_proc = NULL;
worker->wi_launchtime = GetCurrentTimestamp();
......@@ -1500,15 +1515,35 @@ AutoVacWorkerMain(int argc, char *argv[])
{
sigjmp_buf local_sigjmp_buf;
Oid dbid;
bool for_analyze = false;
GpRoleValue orig_role;
am_autovacuum_worker = true;
/* MPP-4990: Autovacuum always runs as utility-mode */
Gp_role = GP_ROLE_UTILITY;
if (IS_QUERY_DISPATCHER() && AutoVacuumingActive())
{
/*
* Gp_role for the current autovacuum worker should be determined by wi_for_analyze.
* But we don't know the value of wi_for_analyze now, so we set Gp_role to
* GP_ROLE_DISPATCH first. Gp_role will switch to GP_ROLE_UTILITY as needed
* after we get the wi_for_analyze.
*/
Gp_role = GP_ROLE_DISPATCH;
}
orig_role = Gp_role;
/* Identify myself via ps */
init_ps_display("autovacuum worker process", "", "", "");
/*
* PreAuthDelay is a debugging aid for investigating problems in the
* authentication cycle.
*/
if (PreAuthDelay > 0)
pg_usleep(PreAuthDelay * 1000000L);
SetProcessingMode(InitProcessing);
/*
......@@ -1553,6 +1588,7 @@ AutoVacWorkerMain(int argc, char *argv[])
*/
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
Gp_role = orig_role;
/* Prevents interrupts while cleaning up */
HOLD_INTERRUPTS();
......@@ -1628,6 +1664,7 @@ AutoVacWorkerMain(int argc, char *argv[])
{
MyWorkerInfo = AutoVacuumShmem->av_startingWorker;
dbid = MyWorkerInfo->wi_dboid;
for_analyze = MyWorkerInfo->wi_for_analyze;
MyWorkerInfo->wi_proc = MyProc;
/* insert into the running list */
......@@ -1689,7 +1726,16 @@ AutoVacWorkerMain(int argc, char *argv[])
/* And do an appropriate amount of work */
recentXid = ReadNewTransactionId();
recentMulti = ReadNextMultiXactId();
AssertImply(!IS_QUERY_DISPATCHER(), for_analyze == false);
AssertImply(for_analyze, IS_QUERY_DISPATCHER());
if (!for_analyze && orig_role == GP_ROLE_DISPATCH)
Gp_role = GP_ROLE_UTILITY;
do_autovacuum();
if (!for_analyze && orig_role == GP_ROLE_DISPATCH)
Gp_role = orig_role;
}
/*
......@@ -1732,6 +1778,7 @@ FreeWorkerInfo(int code, Datum arg)
MyWorkerInfo->wi_proc = NULL;
MyWorkerInfo->wi_launchtime = 0;
MyWorkerInfo->wi_dobalance = false;
MyWorkerInfo->wi_for_analyze = false;
MyWorkerInfo->wi_cost_delay = 0;
MyWorkerInfo->wi_cost_limit = 0;
MyWorkerInfo->wi_cost_limit_base = 0;
......@@ -1902,8 +1949,11 @@ get_database_list(void)
* with !datallowconn). The administrator is expected to do all
* VACUUMing manually, except for template0, which you cannot
* VACUUM manually because you cannot connect to it.
*
* If autovacuum on the master is enabled, it means that we also want to do
* autoanalyze work for databases whose datallowconn is true.
*/
if (pgdatabase->datallowconn)
if (!(IS_QUERY_DISPATCHER() && AutoVacuumingActive()) && pgdatabase->datallowconn)
continue;
avdb = (avw_dbase *) palloc(sizeof(avw_dbase));
......@@ -1912,6 +1962,7 @@ get_database_list(void)
avdb->adw_name = pstrdup(NameStr(pgdatabase->datname));
avdb->adw_frozenxid = pgdatabase->datfrozenxid;
avdb->adw_minmulti = pgdatabase->datminmxid;
avdb->adw_allowconn = pgdatabase->datallowconn;
/* this gets set later: */
avdb->adw_entry = NULL;
......@@ -2085,19 +2136,14 @@ do_autovacuum(void)
/*
* Check if it is a temp table (presumably, of some other backend's).
* We cannot safely process other backends' temp tables.
*
* GPDB: Currently we enable autovacuum ANALYZE on QD(Master), so the below
* code only process temp tables on Master. Segments' temp tables still
* need to be considered in future.
*/
if (classForm->relpersistence == RELPERSISTENCE_TEMP)
{
int backendID;
/*
* GPDB_91_MERGE_FIXME: Autovacuum operates only on template0
* database in Greenplum. We expect no temp tables in template0.
* Whenever we allow autovacuum to operate on user databases, we
* must deal with the logic to detect other backend's temp tables
* below.
*/
Assert(false);
backendID = GetTempNamespaceBackendId(classForm->relnamespace);
......@@ -2445,6 +2491,12 @@ do_autovacuum(void)
FlushErrorState();
MemoryContextResetAndDeleteChildren(PortalContext);
#ifdef FAULT_INJECTOR
FaultInjector_InjectFaultIfSet(
"auto_vac_worker_abort", DDLNotSpecified,
"", tab->at_relname);
#endif
/* restart our transaction for the following operations */
StartTransactionCommand();
RESUME_INTERRUPTS();
......@@ -2789,6 +2841,14 @@ relation_needs_vacanalyze(Oid relid,
int multixact_freeze_max_age;
TransactionId xidForceLimit;
MultiXactId multiForceLimit;
bool for_analyze;
/*
* We don't need to hold AutovacuumLock here, since it should be read-only in the worker itself
* once the MyWorkerInfo gets set, all the workers only care about its own value.
*/
Assert(MyWorkerInfo);
for_analyze = MyWorkerInfo->wi_for_analyze;
AssertArg(classForm != NULL);
AssertArg(OidIsValid(relid));
......@@ -2894,6 +2954,30 @@ relation_needs_vacanalyze(Oid relid,
/* ANALYZE refuses to work with pg_statistics */
if (relid == StatisticRelationId)
*doanalyze = false;
/*
* The Gp_role will be GP_ROLE_UTILITY when for_analyze is false, and do ANALYZE
* in utility mode is useless, so just disable analyze here.
*/
if (!for_analyze)
*doanalyze = false;
else
{
*dovacuum = false;
*wraparound = false;
}
/*
* There are a lot of things to do to enable auto-ANALYZE for partition tables,
* see PR10515 for details.
* Currently, we just disable auto-ANALYZE for partition tables.
*/
if (*doanalyze)
{
Assert(for_analyze && Gp_role == GP_ROLE_DISPATCH);
if (rel_part_status(relid) != PART_STATUS_NONE)
*doanalyze = false;
}
}
/*
......@@ -2922,6 +3006,12 @@ autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
/* Let pgstat know what we're doing */
autovac_report_activity(tab);
#ifdef FAULT_INJECTOR
FaultInjector_InjectFaultIfSet(
"auto_vac_worker_after_report_activity", DDLNotSpecified,
"", tab->at_relname);
#endif
vacuum(tab->at_vacoptions, &rangevar, tab->at_relid, &tab->at_params, NIL,
bstrategy, true);
}
......
......@@ -68,9 +68,13 @@
#include "utils/snapmgr.h"
#include "utils/timestamp.h"
#include "utils/tqual.h"
#include "utils/lsyscache.h"
#include "cdb/cdbvars.h"
#include "cdb/cdbpartition.h"
#include "commands/resgroupcmds.h"
#include "utils/faultinjector.h"
/* ----------
* Timer definitions.
......@@ -5965,3 +5969,101 @@ pgstat_db_requested(Oid databaseid)
return false;
}
/*
* just as pgstat_count_heap_update(), with an extra parameter ntuples.
* ntuples is used to specify how many rows the caller has updated.
*/
static void
gp_pgstat_count_heap_update(PgStat_TableStatus *pgstat_info, PgStat_Counter ntuples)
{
int nest_level = GetCurrentTransactionNestLevel();
if (pgstat_info->trans == NULL || pgstat_info->trans->nest_level != nest_level)
add_tabstat_xact_level(pgstat_info, nest_level);
pgstat_info->trans->tuples_updated += ntuples;
}
/*
* just as pgstat_count_heap_delete(), with an extra parameter ntuples.
* ntuples is used to specify how many rows the caller has deleted.
*/
static void
gp_pgstat_count_heap_delete(PgStat_TableStatus *pgstat_info, PgStat_Counter ntuples)
{
int nest_level = GetCurrentTransactionNestLevel();
if (pgstat_info->trans == NULL || pgstat_info->trans->nest_level != nest_level)
add_tabstat_xact_level(pgstat_info, nest_level);
pgstat_info->trans->tuples_deleted += ntuples;
}
/*
* Report the statistics gathered by QD to statistics collector.
* These parameters tell us that 'tuples' rows of table 'reloid' has changed,
* and 'cmdtype' specifies the type of change.
* reloid must have been locked.
*/
void
gp_pgstat_report_tabstat(AutoStatsCmdType cmdtype, Oid reloid, uint64 tuples)
{
if (Gp_role != GP_ROLE_DISPATCH || reloid == InvalidOid || tuples <= 0
|| get_rel_relkind(reloid) != RELKIND_RELATION)
return;
Relation rel = relation_open(reloid, NoLock);
if (!rel->pgstat_info)
{
relation_close(rel, NoLock);
return;
}
switch (cmdtype)
{
case AUTOSTATS_CMDTYPE_CTAS:
case AUTOSTATS_CMDTYPE_INSERT:
case AUTOSTATS_CMDTYPE_COPY:
pgstat_count_heap_insert(rel, tuples);
break;
case AUTOSTATS_CMDTYPE_UPDATE:
gp_pgstat_count_heap_update(rel->pgstat_info, tuples);
break;
case AUTOSTATS_CMDTYPE_DELETE:
gp_pgstat_count_heap_delete(rel->pgstat_info, tuples);
break;
default:
break;
}
#ifdef FAULT_INJECTOR
FaultInjector_InjectFaultIfSet(
"gp_pgstat_report_on_master", DDLNotSpecified,
"", RelationGetRelationName(rel));
#endif
relation_close(rel, NoLock);
}
/*
* collect_tabstat - collect relation's pgstat or do auto_stats.
* it should be called after QD have gathered the number of tuples changed in QEs.
* cmdType specifies the type of change.
*
* ntuples means the processed tuples for a relation at end of a command execution
* on QD.
*
* NOTE: If the autovacuum is enabled on master, collect gpstat for the target
* relation for ANALYZE. Currently this is not support for partition table.
* This is because we don't have accurate pgstat on QD for parent table and child
* table. So run auto_stats for partition table.
* For relation who's storage parameter autovacuum_enable=false with autovacuum
* enabled, neither autovacuum ANALYZE nor auto_stats will execute for it. Users
* need to manually run ANALYZE for it. This is same with Postgres.
*/
void
collect_tabstat(AutoStatsCmdType cmdType, Oid relationOid, uint64 ntuples, bool inFunction)
{
if (Gp_role == GP_ROLE_DISPATCH && AutoVacuumingActive() &&
rel_part_status(relationOid) == PART_STATUS_NONE)
return gp_pgstat_report_tabstat(cmdType, relationOid, ntuples);
else
return auto_stats(cmdType, relationOid, ntuples, inFunction);
}
......@@ -327,11 +327,11 @@ InitProcess(void)
PGPROC *volatile * procgloballist;
/*
* Autovacuum, WAL sender, FTS handler and FTS daemon processes are marked
* WAL sender, FTS handler and FTS daemon processes are marked
* as GP_ROLE_UTILITY to prevent unwanted GP_ROLE_DISPATCH MyProc settings
* such as mppSessionId being valid and mppIsWriter set to true.
*/
if (IsAutoVacuumWorkerProcess() || am_walsender || am_ftshandler ||
if (am_walsender || am_ftshandler ||
IsFaultHandler)
Gp_role = GP_ROLE_UTILITY;
......
......@@ -5055,8 +5055,9 @@ PostgresMain(int argc, char *argv[],
restore_guc_to_QE();
// firstchar may be -1, it will cause AssertFailed if we enable cassert
ereport((Debug_print_full_dtm ? LOG : DEBUG5),
(errmsg_internal("First char: '%c'; gp_role = '%s'.", firstchar, role_to_string(Gp_role))));
(errmsg_internal("First char: '%d'; gp_role = '%s'.", firstchar, role_to_string(Gp_role))));
check_forbidden_in_gpdb_handlers(firstchar);
......
......@@ -39,6 +39,7 @@
#include "utils/resscheduler.h"
#include "utils/metrics_utils.h"
#include "utils/tqual.h"
#include "pgstat.h"
/*
......@@ -321,7 +322,7 @@ ProcessQuery(Portal portal,
{
/* MPP-4082. Issue automatic ANALYZE if conditions are satisfied. */
bool inFunction = false;
auto_stats(cmdType, relationOid, queryDesc->es_processed, inFunction);
collect_tabstat(cmdType, relationOid, queryDesc->es_processed, inFunction);
}
FreeQueryDesc(queryDesc);
......
......@@ -273,7 +273,11 @@ FaultInjector_InjectFaultIfSet_out_of_line(
if (IsAutoVacuumLauncherProcess() ||
(IsAutoVacuumWorkerProcess() &&
!(0 == strcmp("vacuum_update_dat_frozen_xid", faultName) ||
0 == strcmp("auto_vac_worker_before_do_autovacuum", faultName))))
0 == strcmp("auto_vac_worker_before_do_autovacuum", faultName) ||
0 == strcmp("auto_vac_worker_after_report_activity", faultName) ||
0 == strcmp("auto_vac_worker_abort", faultName) ||
0 == strcmp("analyze_after_hold_lock", faultName) ||
0 == strcmp("analyze_finished_one_relation", faultName))))
return FaultInjectorTypeNotSpecified;
/*
......
......@@ -1265,10 +1265,9 @@ static struct config_bool ConfigureNamesBool[] =
},
{
{"autovacuum", PGC_SIGHUP, DEFUNCT_OPTIONS,
{"autovacuum", PGC_SIGHUP, AUTOVACUUM,
gettext_noop("Starts the autovacuum subprocess."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_NO_SHOW_ALL
NULL
},
&autovacuum_start_daemon,
/*
......@@ -2668,10 +2667,10 @@ static struct config_int ConfigureNamesInt[] =
},
{
{"autovacuum_naptime", PGC_SIGHUP, DEFUNCT_OPTIONS,
{"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM,
gettext_noop("Time to sleep between autovacuum runs."),
NULL,
GUC_UNIT_S | GUC_NOT_IN_SAMPLE | GUC_NO_SHOW_ALL
GUC_UNIT_S
},
&autovacuum_naptime,
60, 1, INT_MAX / 1000,
......@@ -2688,10 +2687,9 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
{"autovacuum_analyze_threshold", PGC_SIGHUP, DEFUNCT_OPTIONS,
{"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM,
gettext_noop("Minimum number of tuple inserts, updates or deletes prior to analyze."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_NO_SHOW_ALL
NULL
},
&autovacuum_anl_thresh,
50, 0, INT_MAX,
......@@ -3033,10 +3031,9 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
{"autovacuum_analyze_scale_factor", PGC_SIGHUP, DEFUNCT_OPTIONS,
{"autovacuum_analyze_scale_factor", PGC_SIGHUP, AUTOVACUUM,
gettext_noop("Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_NO_SHOW_ALL
NULL
},
&autovacuum_anl_scale,
0.1, 0.0, 100.0,
......
......@@ -21,6 +21,8 @@
#include "utils/hsearch.h"
#include "utils/relcache.h"
#include "postmaster/autostats.h"
/* ----------
* Paths for the statistics files (relative to installation's $PGDATA).
......@@ -1028,6 +1030,8 @@ extern void pgstat_ping(void);
extern void pgstat_report_stat(bool force);
extern void pgstat_vacuum_stat(void);
extern void pgstat_report_queuestat(void); /* GPDB */
extern void collect_tabstat(AutoStatsCmdType cmdType, Oid relationOid, uint64 ntuples, bool inFunction); /* GPDB */
extern void pgstat_drop_database(Oid databaseid);
extern void pgstat_clear_snapshot(void);
......@@ -1071,6 +1075,7 @@ extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
extern void pgstat_report_resgroup(Oid groupid);
extern void gp_pgstat_report_tabstat(AutoStatsCmdType cmdtype, Oid reloid, uint64 tuples);
extern void pgstat_initstats(Relation rel);
......
-- Enable auto-ANALYZE only on AUTOVACUUM launcher. When set `autovacuum` to
-- true on Master, the launcher will take care of databases' analyze job on Master.
-- VACUUM anti-XID wraparounds of 'template0' is not changed.
-- Start AUTOVACUUM launcher.
ALTER SYSTEM SET autovacuum = on;
ALTER SYSTEM SET autovacuum_naptime = 5;
select * from pg_reload_conf();
--
-- Test1, sanity test to make sure auto-analyze works
--
-- Analyze all exists relations to prevent autoanalyze on them.
-- These actually don't have too much effort for preventing
-- autoanalyze on other tables, but it could reduce some of tables.
1: ANALYZE;
-- Prepare the table to be ANALYZEd
1: CREATE TABLE anatest (id bigint);
-- Track report gpstat on master
SELECT gp_inject_fault('gp_pgstat_report_on_master', 'suspend', '', '', 'anatest', 1, -1, 0, 1);
-- Track that we have updated the attributes stats in pg_statistic when finished
SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'anatest', 1, -1, 0, 1);
-- Suspend the autovacuum worker from analyze before
SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'anatest', 1, -1, 0, 1);
1&: INSERT INTO anatest select i from generate_series(1, 1000) as i;
-- Wait until report pgstat on master
SELECT gp_wait_until_triggered_fault('gp_pgstat_report_on_master', 1, 1);
SELECT gp_inject_fault('gp_pgstat_report_on_master', 'reset', 1);
1<:
-- Wait until autovacuum is triggered
SELECT gp_wait_until_triggered_fault('auto_vac_worker_after_report_activity', 1, 1);
select datname, query from pg_stat_activity where query like '%ANALYZE%' and datname=current_database();
SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
-- Wait until autovacuum worker updates pg_database
SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
-- Should have statistic updated.
SELECT count(*) FROM pg_statistic where starelid = 'anatest'::regclass;
select relpages, reltuples from pg_class where oid = 'anatest'::regclass;
--
-- Test2, if another transaction want to acquire lock which conflict with
-- auto-analyze's ShareUpdateExclusiveLock, the auto-analyze should abort.
--
1: CREATE TABLE anaabort(id int);
-- Track report gpstat on master
SELECT gp_inject_fault('gp_pgstat_report_on_master', 'suspend', '', '', 'anaabort', 1, -1, 0, 1);
-- Suspend the autovacuum worker from analyze before
SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'anaabort', 1, -1, 0, 1);
-- Suspend the autovacuum worker after holding ShareUpdateExclusiveLock.
SELECT gp_inject_fault('analyze_after_hold_lock', 'infinite_loop', '', '', 'anaabort', 1, -1, 0, 1);
-- Track the autovacuum worker abort for conflict ShareUpdateExclusiveLock.
SELECT gp_inject_fault('auto_vac_worker_abort', 'skip', '', '', 'anaabort', 1, -1, 0, 1);
1&: INSERT INTO anaabort select i from generate_series(1, 1000) as i;
-- Wait until report pgstat on master
SELECT gp_wait_until_triggered_fault('gp_pgstat_report_on_master', 1, 1);
SELECT gp_inject_fault('gp_pgstat_report_on_master', 'reset', 1);
1<:
-- Wait until autovacuum is triggered
SELECT gp_wait_until_triggered_fault('auto_vac_worker_after_report_activity', 1, 1);
select datname, query from pg_stat_activity where query like '%ANALYZE%' and datname=current_database();
SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
-- Wait until autovacuum work hold ShareUpdateExclusiveLock.
SELECT gp_wait_until_triggered_fault('analyze_after_hold_lock', 1, 1);
-- Acquire EXCLUSIVE lock on the analyze table
1: BEGIN;
1&: LOCK TABLE anaabort in EXCLUSIVE mode;
-- The auto-analyze should abort
SELECT gp_wait_until_triggered_fault('auto_vac_worker_abort', 1, 1);
SELECT gp_inject_fault('auto_vac_worker_abort', 'reset', 1);
SELECT gp_inject_fault('analyze_after_hold_lock', 'reset', 1);
-- Get lock
1<:
-- Shouldn't have statistic updated.
SELECT count(*) FROM pg_statistic where starelid = 'anaabort'::regclass;
select relpages, reltuples from pg_class where oid = 'anaabort'::regclass;
1: END;
-- Disable AUTOVACUUM launcher.
ALTER SYSTEM SET autovacuum_naptime = 60;
ALTER SYSTEM SET autovacuum = off;
select * from pg_reload_conf();
test: setup
test: autovacuum-analyze
test: lockmodes
# Put test prepare_limit near to test lockmodes since both of them reboot the
# cluster during testing. Usually the 2nd reboot should be faster.
......
-- Enable auto-ANALYZE only on AUTOVACUUM launcher. When set `autovacuum` to
-- true on Master, the launcher will take care of databases' analyze job on Master.
-- VACUUM anti-XID wraparounds of 'template0' is not changed.
-- Start AUTOVACUUM launcher.
ALTER SYSTEM SET autovacuum = on;
ALTER
ALTER SYSTEM SET autovacuum_naptime = 5;
ALTER
select * from pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
--
-- Test1, sanity test to make sure auto-analyze works
--
-- Analyze all exists relations to prevent autoanalyze on them.
-- These actually don't have too much effort for preventing
-- autoanalyze on other tables, but it could reduce some of tables.
1: ANALYZE;
ANALYZE
-- Prepare the table to be ANALYZEd
1: CREATE TABLE anatest (id bigint);
CREATE
-- Track report gpstat on master
SELECT gp_inject_fault('gp_pgstat_report_on_master', 'suspend', '', '', 'anatest', 1, -1, 0, 1);
gp_inject_fault
-----------------
Success:
(1 row)
-- Track that we have updated the attributes stats in pg_statistic when finished
SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'anatest', 1, -1, 0, 1);
gp_inject_fault
-----------------
Success:
(1 row)
-- Suspend the autovacuum worker from analyze before
SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'anatest', 1, -1, 0, 1);
gp_inject_fault
-----------------
Success:
(1 row)
1&: INSERT INTO anatest select i from generate_series(1, 1000) as i; <waiting ...>
-- Wait until report pgstat on master
SELECT gp_wait_until_triggered_fault('gp_pgstat_report_on_master', 1, 1);
gp_wait_until_triggered_fault
-------------------------------
Success:
(1 row)
SELECT gp_inject_fault('gp_pgstat_report_on_master', 'reset', 1);
gp_inject_fault
-----------------
Success:
(1 row)
1<: <... completed>
INSERT 1000
-- Wait until autovacuum is triggered
SELECT gp_wait_until_triggered_fault('auto_vac_worker_after_report_activity', 1, 1);
gp_wait_until_triggered_fault
-------------------------------
Success:
(1 row)
select datname, query from pg_stat_activity where query like '%ANALYZE%' and datname=current_database();
datname | query
----------------+----------------------------------------------------------------------------------------------------------
isolation2test | select datname, query from pg_stat_activity where query like '%ANALYZE%' and datname=current_database();
isolation2test | autovacuum: ANALYZE public.anatest
(2 rows)
SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
gp_inject_fault
-----------------
Success:
(1 row)
-- Wait until autovacuum worker updates pg_database
SELECT gp_wait_until_triggered_fault('analyze_finished_one_relation', 1, 1);
gp_wait_until_triggered_fault
-------------------------------
Success:
(1 row)
SELECT gp_inject_fault('analyze_finished_one_relation', 'reset', 1);
gp_inject_fault
-----------------
Success:
(1 row)
-- Should have statistic updated.
SELECT count(*) FROM pg_statistic where starelid = 'anatest'::regclass;
count
-------
1
(1 row)
select relpages, reltuples from pg_class where oid = 'anatest'::regclass;
relpages | reltuples
----------+-----------
3 | 1000
(1 row)
--
-- Test2, if another transaction want to acquire lock which conflict with
-- auto-analyze's ShareUpdateExclusiveLock, the auto-analyze should abort.
--
1: CREATE TABLE anaabort(id int);
CREATE
-- Track report gpstat on master
SELECT gp_inject_fault('gp_pgstat_report_on_master', 'suspend', '', '', 'anaabort', 1, -1, 0, 1);
gp_inject_fault
-----------------
Success:
(1 row)
-- Suspend the autovacuum worker from analyze before
SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'anaabort', 1, -1, 0, 1);
gp_inject_fault
-----------------
Success:
(1 row)
-- Suspend the autovacuum worker after holding ShareUpdateExclusiveLock.
SELECT gp_inject_fault('analyze_after_hold_lock', 'infinite_loop', '', '', 'anaabort', 1, -1, 0, 1);
gp_inject_fault
-----------------
Success:
(1 row)
-- Track the autovacuum worker abort for conflict ShareUpdateExclusiveLock.
SELECT gp_inject_fault('auto_vac_worker_abort', 'skip', '', '', 'anaabort', 1, -1, 0, 1);
gp_inject_fault
-----------------
Success:
(1 row)
1&: INSERT INTO anaabort select i from generate_series(1, 1000) as i; <waiting ...>
-- Wait until report pgstat on master
SELECT gp_wait_until_triggered_fault('gp_pgstat_report_on_master', 1, 1);
gp_wait_until_triggered_fault
-------------------------------
Success:
(1 row)
SELECT gp_inject_fault('gp_pgstat_report_on_master', 'reset', 1);
gp_inject_fault
-----------------
Success:
(1 row)
1<: <... completed>
INSERT 1000
-- Wait until autovacuum is triggered
SELECT gp_wait_until_triggered_fault('auto_vac_worker_after_report_activity', 1, 1);
gp_wait_until_triggered_fault
-------------------------------
Success:
(1 row)
select datname, query from pg_stat_activity where query like '%ANALYZE%' and datname=current_database();
datname | query
----------------+----------------------------------------------------------------------------------------------------------
isolation2test | select datname, query from pg_stat_activity where query like '%ANALYZE%' and datname=current_database();
isolation2test | autovacuum: ANALYZE public.anaabort
(2 rows)
SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'reset', 1);
gp_inject_fault
-----------------
Success:
(1 row)
-- Wait until autovacuum work hold ShareUpdateExclusiveLock.
SELECT gp_wait_until_triggered_fault('analyze_after_hold_lock', 1, 1);
gp_wait_until_triggered_fault
-------------------------------
Success:
(1 row)
-- Acquire EXCLUSIVE lock on the analyze table
1: BEGIN;
BEGIN
1&: LOCK TABLE anaabort in EXCLUSIVE mode; <waiting ...>
-- The auto-analyze should abort
SELECT gp_wait_until_triggered_fault('auto_vac_worker_abort', 1, 1);
gp_wait_until_triggered_fault
-------------------------------
Success:
(1 row)
SELECT gp_inject_fault('auto_vac_worker_abort', 'reset', 1);
gp_inject_fault
-----------------
Success:
(1 row)
SELECT gp_inject_fault('analyze_after_hold_lock', 'reset', 1);
gp_inject_fault
-----------------
Success:
(1 row)
-- Get lock
1<: <... completed>
LOCK
-- Shouldn't have statistic updated.
SELECT count(*) FROM pg_statistic where starelid = 'anaabort'::regclass;
count
-------
0
(1 row)
select relpages, reltuples from pg_class where oid = 'anaabort'::regclass;
relpages | reltuples
----------+-----------
0 | 0
(1 row)
1: END;
END
-- Disable AUTOVACUUM launcher.
ALTER SYSTEM SET autovacuum_naptime = 60;
ALTER
ALTER SYSTEM SET autovacuum = off;
ALTER
select * from pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
......@@ -252,6 +252,7 @@ test: oid_wraparound
test: fts_recovery_in_progress
test: mirror_replay
test: autovacuum-template0
test: collect_tabstat
# gpexpand introduce the partial tables, check them if they can run correctly
test: gangsize gang_reuse
......
\t on
\a
set client_min_messages to warning;
-- Enable AUTOVACUUM launcher
ALTER SYSTEM SET autovacuum = on;
select pg_reload_conf();
-- Test collect_tabstat() in copy.
create table table_for_docopy(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
copy table_for_docopy (i, j) from stdin;
2 hello2
1 hello1
3 hello3
\.
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_docopy'::regclass;
-- Test collect_tabstat() in CTAS
create table table_for_ctas with (autovacuum_enabled=false) as select i, 'hello' || i from generate_series(1, 100) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_ctas'::regclass;
select i, 'hello' || i into table_for_insert_into from generate_series(1, 100) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_insert_into'::regclass;
-- Test collect_tabstat() in ALTER TABLE SET DISTRIBUTED BY
create table table_for_set_distributed_by(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
insert into table_for_set_distributed_by select i, 'hello' || i from generate_series(1, 333) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_set_distributed_by'::regclass;
alter table table_for_set_distributed_by set distributed by (j);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_set_distributed_by'::regclass;
-- Test collect_tabstat() in execution of funciton.
create table table_for_function(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
create function update_table_for_function() returns void as
$$
begin
insert into table_for_function select i, 'hello' || i from generate_series(1, 333) f(i);
delete from table_for_function where i <= 200;
end
$$
language plpgsql volatile execute on master;
select update_table_for_function();
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_function'::regclass;
-- Test collect_tabstat() in ALTER TABLE EXPAND TABLE;
-- Thanks @uglthinx for telling me this way to execute ALTER TABLE EXPAND manually
create table table_for_expand(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
set allow_system_table_mods=true;
update gp_distribution_policy set numsegments = 1 where localoid = 'table_for_expand'::regclass;
set allow_system_table_mods to default;
insert into table_for_expand select i, 'hello' || i from generate_series(1, 333) f(i);
select count(distinct gp_segment_id) from table_for_expand;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_expand'::regclass;
alter table table_for_expand expand table;
select count(distinct gp_segment_id) from table_for_expand;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_expand'::regclass;
-- Test collect_tabstat() in INSERT/UPDATE/DELETE
create table table_for_iud(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
begin;
insert into table_for_iud select i, 'hello' || i from generate_series(1, 333) f(i);
savepoint level1;
savepoint level2;
savepoint level3;
savepoint level4;
savepoint level5;
savepoint level6;
savepoint level7;
delete from table_for_iud where i <= 200;
rollback to savepoint level5;
update table_for_iud set j = 'heroes never die' where i >= 300;
release savepoint level3;
commit;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_iud'::regclass;
begin;
savepoint level1;
savepoint level2;
savepoint level3;
savepoint level4;
savepoint level5;
savepoint level6;
savepoint level7;
insert into table_for_iud select i, 'hello' || i from generate_series(333, 777) f(i);
rollback to savepoint level5;
update table_for_iud set j = 'are you ok' where i >= 300;
rollback to savepoint level3;
delete from table_for_iud where i <= 200;
commit;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_iud'::regclass;
-- Test collect_tabstat in TRUNCATE
create table table_for_truncate(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
insert into table_for_truncate select i, 'hello' || i from generate_series(1, 777) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_truncate'::regclass;
begin;
savepoint level1;
savepoint level2;
savepoint level3;
update table_for_truncate set j = 'D' where i <= 200;
delete from table_for_truncate where i >= 700;
insert into table_for_truncate select i, 'hello' || i from generate_series(778, 800) f(i);
truncate table_for_truncate;
insert into table_for_truncate select i, 'hello' || i from generate_series(1, 800) f(i);
delete from table_for_truncate where i >= 700;
update table_for_truncate set j = 'D' where i <= 200;
commit;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_truncate'::regclass;
create table table_for_truncate_abort(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
insert into table_for_truncate_abort select i, 'hello' || i from generate_series(1, 777) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_truncate_abort'::regclass;
begin;
savepoint level1;
savepoint level2;
savepoint level3;
update table_for_truncate_abort set j = 'D' where i <= 200;
delete from table_for_truncate_abort where i >= 700;
insert into table_for_truncate_abort select i, 'hello' || i from generate_series(778, 800) f(i);
truncate table_for_truncate_abort;
insert into table_for_truncate_abort select i, 'hello' || i from generate_series(1, 800) f(i);
delete from table_for_truncate_abort where i < 700;
update table_for_truncate_abort set j = 'D' where i >= 200;
rollback;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_truncate_abort'::regclass;
-- Disable AUTOVACUUM launcher
ALTER SYSTEM SET autovacuum = off;
select pg_reload_conf();
\t on
\a
set client_min_messages to warning;
-- Enable AUTOVACUUM launcher
ALTER SYSTEM SET autovacuum = on;
select pg_reload_conf();
t
-- Test collect_tabstat() in copy.
create table table_for_docopy(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
copy table_for_docopy (i, j) from stdin;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_docopy'::regclass;
3|0|0|0|3|0|3
-- Test collect_tabstat() in CTAS
create table table_for_ctas with (autovacuum_enabled=false) as select i, 'hello' || i from generate_series(1, 100) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_ctas'::regclass;
100|0|0|0|100|0|100
select i, 'hello' || i into table_for_insert_into from generate_series(1, 100) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_insert_into'::regclass;
100|0|0|0|100|0|100
-- Test collect_tabstat() in ALTER TABLE SET DISTRIBUTED BY
create table table_for_set_distributed_by(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
insert into table_for_set_distributed_by select i, 'hello' || i from generate_series(1, 333) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_set_distributed_by'::regclass;
333|0|0|0|333|0|333
alter table table_for_set_distributed_by set distributed by (j);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_set_distributed_by'::regclass;
333|0|0|0|333|0|333
-- Test collect_tabstat() in execution of funciton.
create table table_for_function(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
create function update_table_for_function() returns void as
$$
begin
insert into table_for_function select i, 'hello' || i from generate_series(1, 333) f(i);
delete from table_for_function where i <= 200;
end
$$
language plpgsql volatile execute on master;
select update_table_for_function();
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_function'::regclass;
333|0|200|0|133|200|533
-- Test collect_tabstat() in ALTER TABLE EXPAND TABLE;
-- Thanks @uglthinx for telling me this way to execute ALTER TABLE EXPAND manually
create table table_for_expand(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
set allow_system_table_mods=true;
update gp_distribution_policy set numsegments = 1 where localoid = 'table_for_expand'::regclass;
set allow_system_table_mods to default;
insert into table_for_expand select i, 'hello' || i from generate_series(1, 333) f(i);
select count(distinct gp_segment_id) from table_for_expand;
1
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_expand'::regclass;
333|0|0|0|333|0|333
alter table table_for_expand expand table;
select count(distinct gp_segment_id) from table_for_expand;
3
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_expand'::regclass;
333|0|0|0|333|0|333
-- Test collect_tabstat() in INSERT/UPDATE/DELETE
create table table_for_iud(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
begin;
insert into table_for_iud select i, 'hello' || i from generate_series(1, 333) f(i);
savepoint level1;
savepoint level2;
savepoint level3;
savepoint level4;
savepoint level5;
savepoint level6;
savepoint level7;
delete from table_for_iud where i <= 200;
rollback to savepoint level5;
update table_for_iud set j = 'heroes never die' where i >= 300;
release savepoint level3;
commit;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_iud'::regclass;
333|34|200|0|333|34|367
begin;
savepoint level1;
savepoint level2;
savepoint level3;
savepoint level4;
savepoint level5;
savepoint level6;
savepoint level7;
insert into table_for_iud select i, 'hello' || i from generate_series(333, 777) f(i);
rollback to savepoint level5;
update table_for_iud set j = 'are you ok' where i >= 300;
rollback to savepoint level3;
delete from table_for_iud where i <= 200;
commit;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_iud'::regclass;
778|68|400|0|133|713|567
-- Test collect_tabstat in TRUNCATE
create table table_for_truncate(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
insert into table_for_truncate select i, 'hello' || i from generate_series(1, 777) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_truncate'::regclass;
777|0|0|0|777|0|777
begin;
savepoint level1;
savepoint level2;
savepoint level3;
update table_for_truncate set j = 'D' where i <= 200;
delete from table_for_truncate where i >= 700;
insert into table_for_truncate select i, 'hello' || i from generate_series(778, 800) f(i);
truncate table_for_truncate;
insert into table_for_truncate select i, 'hello' || i from generate_series(1, 800) f(i);
delete from table_for_truncate where i >= 700;
update table_for_truncate set j = 'D' where i <= 200;
commit;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_truncate'::regclass;
1577|200|101|0|699|301|1878
create table table_for_truncate_abort(i int, j varchar) with (autovacuum_enabled=false) distributed by (i);
insert into table_for_truncate_abort select i, 'hello' || i from generate_series(1, 777) f(i);
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_truncate_abort'::regclass;
777|0|0|0|777|0|777
begin;
savepoint level1;
savepoint level2;
savepoint level3;
update table_for_truncate_abort set j = 'D' where i <= 200;
delete from table_for_truncate_abort where i >= 700;
insert into table_for_truncate_abort select i, 'hello' || i from generate_series(778, 800) f(i);
truncate table_for_truncate_abort;
insert into table_for_truncate_abort select i, 'hello' || i from generate_series(1, 800) f(i);
delete from table_for_truncate_abort where i < 700;
update table_for_truncate_abort set j = 'D' where i >= 200;
rollback;
select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat.
select n_tup_ins, n_tup_upd, n_tup_del, n_tup_hot_upd, n_live_tup, n_dead_tup, n_mod_since_analyze from pg_stat_all_tables_internal where relid = 'table_for_truncate_abort'::regclass;
800|200|78|0|777|223|777
-- Disable AUTOVACUUM launcher
ALTER SYSTEM SET autovacuum = off;
select pg_reload_conf();
t
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册