*** a/doc/src/sgml/config.sgml --- b/doc/src/sgml/config.sgml *************** *** 5148,5153 **** COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; --- 5148,5168 ---- + + multixact_freeze_table_age (integer) + + multixact_freeze_table_age configuration parameter + + + + VACUUM performs a whole-table scan if the table's + pg_class.relminmxid field has reached + the age specified by this setting. The default is 5 million multixacts. + For more information see . + + + + vacuum_freeze_min_age (integer) *************** *** 5169,5174 **** COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; --- 5184,5205 ---- + + multixact_freeze_min_age (integer) + + multixact_freeze_min_age configuration parameter + + + + Specifies the cutoff age (in multixacts) that VACUUM + should use to decide whether to replace multixact IDs with a newer + transaction ID or multixact ID while scanning a table. The default + is 1 million multixacts. + For more information see . + + + + bytea_output (enum) *** a/doc/src/sgml/maintenance.sgml --- b/doc/src/sgml/maintenance.sgml *************** *** 599,604 **** HINT: Stop the postmaster and use a standalone backend to VACUUM in "mydb". --- 599,632 ---- page for details about using a single-user backend. + + Multixacts and Wraparound + + + Multixact ID + wraparound + + + + + Similar to transaction IDs, Multixact IDs are implemented as a 32-bit + counter and corresponding storage which requires careful aging management, + storage cleanup, and wraparound handling. Multixacts are used to implement + row locking by multiple transactions: since there is limited space in the + tuple header to store lock information, that information is stored separately + and only a reference to it is in the tuple header. As with transaction IDs, + VACUUM is in charge of removing old values. Each + VACUUM run sets a mark in each table that indicates what's the + oldest possible value still stored in it; every time this value is older than + , a full-table scan is forced. + Any Multixact older than is + replaced by something else, which can be the zero value, a lone transaction ID, + or a newer Multixact. Eventually, as all tables in all databases have been + scanned and their oldest Multixact values are advanced, on-disk storage for + Multixact can be removed. + + *** a/src/backend/commands/vacuum.c --- b/src/backend/commands/vacuum.c *************** *** 55,60 **** --- 55,62 ---- */ int vacuum_freeze_min_age; int vacuum_freeze_table_age; + int multixact_freeze_min_age; + int multixact_freeze_table_age; /* A few variables that don't seem worth passing around as parameters */ *************** *** 406,411 **** vacuum_set_xid_limits(int freeze_min_age, --- 408,414 ---- MultiXactId *mxactFullScanLimit) { int freezemin; + int mxid_freezemin; TransactionId limit; TransactionId safeLimit; MultiXactId mxactLimit; *************** *** 462,472 **** vacuum_set_xid_limits(int freeze_min_age, *freezeLimit = limit; /* ! * simplistic MultiXactId removal limit: use the same policy as for ! * freezing Xids (except we use the oldest known mxact instead of the ! * current next value). */ ! mxactLimit = GetOldestMultiXactId() - freezemin; if (mxactLimit < FirstMultiXactId) mxactLimit = FirstMultiXactId; *multiXactCutoff = mxactLimit; --- 465,475 ---- *freezeLimit = limit; /* ! * Determine the minimum multixact freeze age to use: as specified by ! * caller, or multixact_freeze_min_age. */ ! mxid_freezemin = Min(freeze_min_age, multixact_freeze_min_age); ! mxactLimit = GetOldestMultiXactId() - mxid_freezemin; if (mxactLimit < FirstMultiXactId) mxactLimit = FirstMultiXactId; *multiXactCutoff = mxactLimit; *************** *** 503,516 **** vacuum_set_xid_limits(int freeze_min_age, /* * Compute MultiXactId limit to cause a full-table vacuum, being * careful not to generate an invalid multi. We just copy the logic ! * (and limits) from plain XIDs here. */ mxactLimit = ReadNextMultiXactId() - freezetable; if (mxactLimit < FirstMultiXactId) mxactLimit = FirstMultiXactId; *mxactFullScanLimit = mxactLimit; } } /* --- 506,524 ---- /* * Compute MultiXactId limit to cause a full-table vacuum, being * careful not to generate an invalid multi. We just copy the logic ! * from plain XIDs here. */ + freezetable = multixact_freeze_table_age; mxactLimit = ReadNextMultiXactId() - freezetable; if (mxactLimit < FirstMultiXactId) mxactLimit = FirstMultiXactId; *mxactFullScanLimit = mxactLimit; } + else + { + Assert(mxactFullScanLimit == NULL); + } } /* *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** *** 1907,1912 **** static struct config_int ConfigureNamesInt[] = --- 1907,1932 ---- }, { + {"multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."), + NULL + }, + &multixact_freeze_min_age, + 1000000, 0, 200000000, + NULL, NULL, NULL + }, + + { + {"multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."), + NULL + }, + &multixact_freeze_table_age, + 5000000, 0, 200000000, + NULL, NULL, NULL + }, + + { {"vacuum_defer_cleanup_age", PGC_SIGHUP, REPLICATION_MASTER, gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."), NULL *** a/src/backend/utils/misc/postgresql.conf.sample --- b/src/backend/utils/misc/postgresql.conf.sample *************** *** 492,497 **** --- 492,499 ---- #lock_timeout = 0 # in milliseconds, 0 is disabled #vacuum_freeze_min_age = 50000000 #vacuum_freeze_table_age = 150000000 + #multixact_freeze_min_age = 1000000 + #multixact_freeze_table_age = 5000000 #bytea_output = 'hex' # hex, escape #xmlbinary = 'base64' #xmloption = 'content' *** a/src/include/commands/vacuum.h --- b/src/include/commands/vacuum.h *************** *** 136,141 **** extern PGDLLIMPORT int default_statistics_target; /* PGDLLIMPORT for --- 136,143 ---- * PostGIS */ extern int vacuum_freeze_min_age; extern int vacuum_freeze_table_age; + extern int multixact_freeze_min_age; + extern int multixact_freeze_table_age; /* in commands/vacuum.c */