Re: shared-memory based stats collector
Andres Freund <andres@anarazel.de>
Hi,
> From 88740269660d00d548910c2f3aa631878c7cf0d4 Mon Sep 17 00:00:00 2001
> From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
> Date: Thu, 21 Feb 2019 12:42:07 +0900
> Subject: [PATCH 4/6] Allow dsm to use on postmaster.
>
> DSM is inhibited to be used on postmaster. Shared memory baesd stats
> collector needs it to work on postmaster and no problem found to do
> that. Just allow it.
Maybe I'm missing something, but why? postmaster doesn't actually need
to process stats messages in any way?
> From 774b1495136db1ad6d174ab261487fdf6cb6a5ed Mon Sep 17 00:00:00 2001
> From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
> Date: Thu, 21 Feb 2019 12:44:56 +0900
> Subject: [PATCH 5/6] Shared-memory based stats collector
>
> Previously activity statistics is shared via files on disk. Every
> backend sends the numbers to the stats collector process via a socket.
> It makes snapshots as a set of files on disk with a certain interval
> then every backend reads them as necessary. It worked fine for
> comparatively small set of statistics but the set is under the
> pressure to growing up and the file size has reached the order of
> megabytes. To deal with larger statistics set, this patch let backends
> directly share the statistics via shared memory.
Btw, you can make the life of a committer easier by collecting the
reviewers and co-authors of a patch yourself...
This desparately needs an introductory comment in pgstat.c or such
explaining how the new scheme works.
> +LWLock StatsMainLock;
> +#define StatsLock (&StatsMainLock)
Wait, what? You can't just define a lock this way. That's process local
memory, locking that doesn't do anything useful.
> +/* Shared stats bootstrap information */
> +typedef struct StatsShmemStruct {
Please note that in PG's coding style the { comes in the next line.
> +/*
> + * Backends store various database-wide info that's waiting to be flushed out
> + * to shared memory in these variables.
> + */
> +static int n_deadlocks = 0;
> +static size_t n_tmpfiles = 0;
> +static size_t n_tmpfilesize = 0;
> +
> +/*
> + * have_recovery_conflicts represents the existence of any kind if conflict
> + */
> +static bool have_recovery_conflicts = false;
> +static int n_conflict_tablespace = 0;
> +static int n_conflict_lock = 0;
> +static int n_conflict_snapshot = 0;
> +static int n_conflict_bufferpin = 0;
> +static int n_conflict_startup_deadlock = 0;
Probably worthwhile to group those into a struct, even just to make
debugging easier.
>
> -/* ----------
> - * pgstat_init() -
> - *
> - * Called from postmaster at startup. Create the resources required
> - * by the statistics collector process. If unable to do so, do not
> - * fail --- better to let the postmaster start with stats collection
> - * disabled.
> - * ----------
> - */
> -void
> -pgstat_init(void)
> +static void
> +pgstat_postmaster_shutdown(int code, Datum arg)
You can't have a function like that without explaining why it's there.
> + /* trash the stats on crash */
> + if (code == 0)
> + pgstat_write_statsfiles();
> }
And especially not without documenting what that code is supposed to
mean.
> pgstat_report_stat(bool force)
> {
> - /* we assume this inits to all zeroes: */
> - static const PgStat_TableCounts all_zeroes;
> - static TimestampTz last_report = 0;
> -
> + static TimestampTz last_flush = 0;
> + static TimestampTz pending_since = 0;
> TimestampTz now;
> - PgStat_MsgTabstat regular_msg;
> - PgStat_MsgTabstat shared_msg;
> - TabStatusArray *tsa;
> - int i;
> + pgstat_flush_stat_context cxt = {0};
> + bool have_other_stats = false;
> + bool pending_stats = false;
> + long elapsed;
> + long secs;
> + int usecs;
> +
> + /* Do we have anything to flush? */
> + if (have_recovery_conflicts || n_deadlocks != 0 || n_tmpfiles != 0)
> + have_other_stats = true;
>
> /* Don't expend a clock check if nothing to do */
> if ((pgStatTabList == NULL || pgStatTabList->tsa_used == 0) &&
> pgStatXactCommit == 0 && pgStatXactRollback == 0 &&
> - !have_function_stats)
> - return;
> + !have_other_stats && !have_function_stats)
> + return 0;
"other" seems like a pretty mysterious category. Seems better to either
name precisely, or just use the underlying variables for checks.
> +/* -------
> + * Subroutines for pgstat_flush_stat.
> + * -------
> + */
> +
> +/*
> + * snapshot_statentry() - Find an entry from source dshash with cache.
> + *
Is snapshot_statentry() really a subroutine for pgstat_flush_stat()?
> +static void *
> +snapshot_statentry(pgstat_snapshot_cxt *cxt, Oid key)
> +{
> + char *lentry = NULL;
> + size_t keysize = cxt->dsh_params->key_size;
> + size_t dsh_entrysize = cxt->dsh_params->entry_size;
> + bool found;
> + bool *negative;
> +
> + /* caches the result entry */
>
> /*
> - * Don't send a message unless it's been at least PGSTAT_STAT_INTERVAL
> - * msec since we last sent one, or the caller wants to force stats out.
> + * Create new hash with arbitrary initial entries since we don't know how
> + * this hash will grow. The boolean put at the end of the entry is
> + * negative flag.
> */
That, uh, seems pretty ugly and hard to understand.
> +/*
> + * pgstat_flush_stat: Flushes table stats out to shared statistics.
> + *
> + * If nowait is true, returns with false if required lock was not acquired
s/with false/false/
> + * immediately. In the case, infos of some tables may be left alone in TSA to
TSA? I assume TabStatusArray, but I don't think that's a common or
useful abbreviation. It'd be ok to just refer to the variable name.
> +static bool
> +pgstat_flush_stat(pgstat_flush_stat_context *cxt, bool nowait)
> +{
> + /* try to apply the tab stats */
> + if (!pgstat_flush_tabstat(cxt, nowait, entry))
> {
> - pgstat_send_tabstat(this_msg);
> - this_msg->m_nentries = 0;
> + /*
> + * Failed. Leave it alone filling at the beginning in TSA.
> + */
> + TabStatHashEntry *hash_entry;
> + bool found;
> +
> + if (new_tsa_hash == NULL)
> + new_tsa_hash = create_tabstat_hash();
> +
> + /* Create hash entry for this entry */
> + hash_entry = hash_search(new_tsa_hash, &entry->t_id,
> + HASH_ENTER, &found);
> + Assert(!found);
> +
> + /*
> + * Move insertion pointer to the next segment. There must be
> + * enough space segments since we are just leaving some of the
> + * current elements.
> + */
> + if (dest_elem >= TABSTAT_QUANTUM)
> + {
> + Assert(dest_tsa->tsa_next != NULL);
> + dest_tsa = dest_tsa->tsa_next;
> + dest_elem = 0;
> + }
> +
> + /* Move the entry if needed */
> + if (tsa != dest_tsa || i != dest_elem)
> + {
> + PgStat_TableStatus *new_entry;
> + new_entry = &dest_tsa->tsa_entries[dest_elem];
> + *new_entry = *entry;
> + entry = new_entry;
> + }
> +
> + hash_entry->tsa_entry = entry;
> + dest_elem++;
This seems a lot of work for just leaving an entry around to be
processed later. Shouldn't code for that already exist elsewhere?
> void
> pgstat_vacuum_stat(void)
> {
> - HTAB *htab;
> - PgStat_MsgTabpurge msg;
> - PgStat_MsgFuncpurge f_msg;
> - HASH_SEQ_STATUS hstat;
> + HTAB *oidtab;
> + dshash_table *dshtable;
> + dshash_seq_status dshstat;
> PgStat_StatDBEntry *dbentry;
> PgStat_StatTabEntry *tabentry;
> PgStat_StatFuncEntry *funcentry;
> - int len;
>
> - if (pgStatSock == PGINVALID_SOCKET)
> + /* we don't collect statistics under standalone mode */
> + if (!IsUnderPostmaster)
> return;
>
> - /*
> - * If not done for this transaction, read the statistics collector stats
> - * file into some hash tables.
> - */
> - backend_read_statsfile();
> + /* If not done for this transaction, take a snapshot of stats */
> + pgstat_snapshot_global_stats();
Hm, why do we need a snapshot here?
> /*
> * Now repeat the above steps for functions. However, we needn't bother
> * in the common case where no function stats are being collected.
> */
Can't we move the act of iterating through these hashes and probing
against another hash into a helper function and reuse? These
duplications aren't pretty.
Greetings,
Andres Freund
Commits
-
Improve comment about dropped entries in pgstat.c
- a0ab20f16541 15.11 landed
- ec194b448cbc 16.7 landed
- bb93b33d7e39 17.3 landed
- 001a537b83ec 18.0 landed
-
Fix temporary memory leak in system table index scans
- 1acf10549e64 18.0 cited
-
pgstat: set timestamps of fixed-numbered stats after a crash.
- 5cd1c40b3ce9 15.0 landed
-
pgstat: Update docs to match the shared memory stats reality.
- b3abca68106d 15.0 landed
-
pgstat: Hide instability in stats.spec with -DCATCACHE_FORCE_RELEASE.
- d6c0db14836c 15.0 cited
-
pgstat: add/extend tests for resetting various kinds of stats.
- 5264add78478 15.0 landed
-
Add minimal tests for recovery conflict handling.
- 9f8a050f68dc 15.0 landed
-
pgstat: test stats interactions with physical replication.
- 53b9cd20d414 15.0 landed
-
pgstat: add tests for handling of restarts, including crashes.
- 16acf7f1aaea 15.0 landed
-
pgstat: add tests for transaction behaviour, 2PC, function stats.
- e349c95d3e91 15.0 landed
-
pgstat: add pg_stat_have_stats() test helper.
- ad401664b801 15.0 landed
-
pgstat: add pg_stat_force_next_flush(), use it to simplify tests.
- 0f96965c6581 15.0 landed
-
pgstat: move pgstat.c to utils/activity.
- fbfe6910eca0 15.0 landed
-
pgstat: store statistics in shared memory.
- 5891c7a8ed8f 15.0 landed
-
pgstat: remove stats_temp_directory.
- 6f0cf87872ab 15.0 landed
-
pgstat: rename STATS_COLLECTOR GUC group to STATS_CUMULATIVE.
- 1db4e5a4eeec 15.0 landed
-
pgstat: revise replication slot API in preparation for shared memory stats.
- e41aed674f35 15.0 landed
-
pgstat: scaffolding for transactional stats creation / drop.
- 8b1dccd37c71 15.0 landed
-
pgstat: introduce PgStat_Kind enum.
- 997afad89d12 15.0 landed
-
pgstat: prepare APIs used by pgstatfuncs for shared memory stats.
- 8fb580a35ce3 15.0 landed
-
pgstat: add pgstat_copy_relation_stats().
- 8ea7963fc741 15.0 landed
-
pgstat: rename some pgstat_send_* functions to pgstat_report_*.
- cc96373cf39b 15.0 landed
-
pgstat: stats collector references in comments.
- bdbd3d9064f9 15.0 landed
-
pgstat: move transactional code into pgstat_xact.c.
- ab62a642d52c 15.0 landed
-
dsm: allow use in single user mode.
- 46a2d2499a64 15.0 landed
-
dshash: revise sequential scan support.
- 909eebf27b9e 15.0 landed
-
pgstat: remove some superflous comments from pgstat.h.
- 55e566fc4bc8 15.0 landed
-
pgstat: reorder pgstat.[ch] contents.
- 315ae75e9b6d 15.0 landed
-
pgstat: split different types of stats into separate files.
- 13619598f108 15.0 landed
-
pgstat: introduce pgstat_relation_should_count().
- 8363102009d8 15.0 landed
-
pgstat: separate "xact level" handling out of relation specific functions.
- d4ba8b51c763 15.0 landed
-
pgstat: rename pgstat_initstats() to pgstat_relation_init().
- bff258a2732e 15.0 landed
-
pgstat: split out WAL handling from pgstat_{initialize,report_stat}.
- 78f9506b380f 15.0 landed
-
pgstat: run pgindent on pgstat.c/h.
- a3a75b982b5b 15.0 landed
-
pgstat: split relation, database handling out of pgstat_report_stat().
- 89c546c29489 15.0 landed
-
Move code around in StartupXLOG().
- be1c00ab13a7 15.0 cited
-
pgstat: Prepare to use mechanism for truncated rels also for droppped rels.
- 6b9501660c93 15.0 landed
-
pgstat: Split out relation stats handling from AtEO[Sub]Xact_PgStat() etc.
- e1f958d759ff 15.0 landed
-
pgstat: Schedule per-backend pgstat shutdown via before_shmem_exit().
- fb2c5028e635 15.0 landed
-
Schedule ShutdownXLOG() in single user mode using before_shmem_exit().
- a1bb3d5dbe6a 15.0 landed
-
Make parallel worker shutdown complete entirely via before_shmem_exit().
- fa91d4c91f28 15.0 landed
-
pgstat: Bring up pgstat in BaseInit() to fix uninitialized use of pgstat by AV.
- ee3f8d3d3aec 15.0 landed
-
pgstat: split reporting/fetching of bgwriter and checkpointer stats.
- 1bc8e7b0991c 15.0 landed
-
Split backend status and progress related functionality out of pgstat.c.
- e1025044cd4e 14.0 landed
-
Split wait event related code from pgstat.[ch] into wait_event.[ch].
- a333476b9251 14.0 landed
-
Make archiver process an auxiliary process.
- d75288fb27b8 14.0 landed
-
Force to send remaining WAL stats to the stats collector at walwriter exit.
- 33394ee6f243 14.0 cited
-
Add pg_stat_database counters for sessions and session time
- 960869da0803 14.0 cited
-
Collect statistics about SLRU caches
- 28cac71bd368 13.0 cited
-
Don't run atexit callbacks in quickdie signal handlers.
- 8e19a82640d3 12.0 cited
-
Create a "fast path" for acquiring weak relation locks.
- 3cba8999b343 9.2.0 cited