Utilizzo della CPU insolitamente elevato

Un’ultima informazione, poi credo che sarò fuori per qualche ora.

root@discourse_app:/# ps aux --sort=-%mem | head -20
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
postgres    4398 17.0 22.0 8216352 6797764 ?     Ss   11:14   9:53 postgres: 15/main: discourse discourse [local] UPDATE
postgres    2729 15.3 21.1 8123888 6517308 ?     Ss   11:13   9:03 postgres: 15/main: discourse discourse [local] UPDATE
postgres    2501 15.9 19.9 8079700 6148812 ?     Ds   11:13   9:25 postgres: 15/main: discourse discourse [local] UPDATE
postgres   22777 16.9 19.5 8084888 6012052 ?     Ds   11:42   4:58 postgres: 15/main: discourse discourse [local] UPDATE
postgres    2753 28.5 11.3 8055000 3482260 ?     Ss   11:13  16:50 postgres: 15/main: discourse discourse [local] idle
postgres   25715  2.9  6.9 7884064 2135536 ?     Ss   11:47   0:44 postgres: 15/main: discourse discourse [local] idle
postgres   20487  2.9  6.6 7885300 2061088 ?     Ss   11:39   0:59 postgres: 15/main: discourse discourse [local] idle
postgres   22055  3.3  6.5 7887336 2012504 ?     Ss   11:41   1:02 postgres: 15/main: discourse discourse [local] idle
postgres   25883  2.5  6.0 7884096 1848424 ?     Ss   11:47   0:38 postgres: 15/main: discourse discourse [local] idle
postgres   28126  2.4  5.6 7883848 1744912 ?     Ss   11:50   0:31 postgres: 15/main: discourse discourse [local] idle
postgres   29365  1.0  4.5 7883084 1386544 ?     Ss   11:52   0:12 postgres: 15/main: discourse discourse [local] idle
postgres   27172  1.6  4.4 7884288 1384664 ?     Ss   11:49   0:22 postgres: 15/main: discourse discourse [local] idle
postgres   25896  2.1  4.4 8034236 1357264 ?     Ss   11:47   0:31 postgres: 15/main: discourse discourse [local] idle
postgres      89  1.7  4.3 7864156 1342760 ?     Ss   11:11   1:04 postgres: 15/main: checkpointer
postgres   28505  1.0  4.2 7884360 1315360 ?     Ss   11:51   0:13 postgres: 15/main: discourse discourse [local] idle
postgres   27175  1.6  4.1 7882780 1277612 ?     Ss   11:49   0:23 postgres: 15/main: discourse discourse [local] idle
postgres   28553  0.9  3.4 7883976 1064964 ?     Ss   11:51   0:11 postgres: 15/main: discourse discourse [local] idle
postgres   30409  1.0  3.3 7882892 1034860 ?     Ss   11:54   0:10 postgres: 15/main: discourse discourse [local] idle
postgres   40651  4.6  1.9 7872036 592152 ?      Ss   12:11   0:03 postgres: 15/main: discourse discourse [local] idle
root@discourse_app:/# redis-cli info memory
# Memory
used_memory:179899224
used_memory_human:171.57M
used_memory_rss:47591424
used_memory_rss_human:45.39M
used_memory_peak:184509776
used_memory_peak_human:175.96M
used_memory_peak_perc:97.50%
used_memory_overhead:3681093
used_memory_startup:948600
used_memory_dataset:176218131
used_memory_dataset_perc:98.47%
allocator_allocated:181437808
allocator_active:182353920
allocator_resident:188317696
allocator_muzzy:0
total_system_memory:31537295360
total_system_memory_human:29.37G
used_memory_lua:58368
used_memory_vm_eval:58368
used_memory_lua_human:57.00K
used_memory_scripts_eval:10304
number_of_cached_scripts:13
number_of_functions:0
number_of_libraries:0
used_memory_vm_functions:33792
used_memory_vm_total:92160
used_memory_vm_total_human:90.00K
used_memory_functions:192
used_memory_scripts:10496
used_memory_scripts_human:10.25K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.00
allocator_frag_bytes:700208
allocator_rss_ratio:1.03
allocator_rss_bytes:5963776
rss_overhead_ratio:0.25
rss_overhead_bytes:-140726272
mem_fragmentation_ratio:0.26
mem_fragmentation_bytes:-132268896
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_total_replication_buffers:0
mem_clients_slaves:0
mem_clients_normal:498197
mem_cluster_links:0
mem_aof_buffer:0
mem_allocator:jemalloc-5.3.0
mem_overhead_db_hashtable_rehashing:0
active_defrag_running:0
lazyfree_pending_objects:0
lazyfreed_objects:0
root@discourse_app:/# cat /etc/postgresql/15/main/postgresql.conf | grep shared_buffers
shared_buffers = 7424MB
#wal_buffers = -1                       # min 32kB, -1 sets based on shared_buffers
root@discourse_app:/# su - postgres -c "psql discourse -c \"SELECT pid, query_start, state, wait_event_type, wait_event, left(query, 100) as query FROM pg_stat_activity WHERE state != 'idle' ORDER BY query_start;\""
  pid  |          query_start          | state  | wait_event_type |  wait_event  |                                                query
-------+-------------------------------+--------+-----------------+--------------+------------------------------------------------------------------------------------------------------
  2501 | 2026-02-07 11:25:01.028892+00 | active | IO              | DataFileRead | UPDATE posts                                                                                        +
       |                               |        |                 |              | SET percent_rank = X.percent_rank                                                                   +
       |                               |        |                 |              | FROM (                                                                                              +
       |                               |        |                 |              |   SELECT posts.id, Y.percent_rank                                                                   +
       |                               |        |                 |              |   FROM posts
  4398 | 2026-02-07 11:52:53.108942+00 | active | IPC             | BufferIO     | WITH eligible_users AS (                                                                            +
       |                               |        |                 |              |   SELECT id                                                                                         +
       |                               |        |                 |              |   FROM users                                                                                        +
       |                               |        |                 |              |   WHERE id > 0 AND active AND silenced_till IS NUL
  2729 | 2026-02-07 11:54:27.666129+00 | active | IPC             | BufferIO     | UPDATE topics AS topics                                                                             +
       |                               |        |                 |              | SET has_summary = (topics.like_count >= 1 AND                                                       +
       |                               |        |                 |              |                    topics.post
 22777 | 2026-02-07 11:59:27.040575+00 | active | IO              | DataFileRead | UPDATE posts                                                                                        +
       |                               |        |                 |              | SET percent_rank = X.percent_rank                                                                   +
       |                               |        |                 |              | FROM (                                                                                              +
       |                               |        |                 |              |   SELECT posts.id, Y.percent_rank                                                                   +
       |                               |        |                 |              |   FROM posts
  27172 | 2026-02-07 12:15:42.50553+00  | active | IO              | DataFileRead | SELECT "posts"."id" FROM "posts" WHERE "posts"."deleted_at" IS NULL AND "posts"."topic_id" = 792311
  25883 | 2026-02-07 12:15:52.665883+00 | active |                 |              | SELECT "posts"."id" FROM "posts" WHERE "posts"."deleted_at" IS NULL AND "posts"."topic_id" = 829626
  20487 | 2026-02-07 12:16:09.733384+00 | active | IO              | DataFileRead | SELECT "posts"."id" FROM "posts" WHERE "posts"."deleted_at" IS NULL AND "posts"."topic_id" = 653216
  42185 | 2026-02-07 12:16:21.053706+00 | active | IO              | DataFileRead | SELECT "posts"."id", "posts"."user_id", "posts"."topic_id", "posts"."post_number", "posts"."raw", "p
  43940 | 2026-02-07 12:16:21.925505+00 | active |                 |              | SELECT pid, query_start, state, wait_event_type, wait_event, left(query, 100) as query FROM pg_stat_
  28126 | 2026-02-07 12:16:21.96218+00  | active | IO              | DataFileRead | SELECT "posts"."id" FROM "posts" WHERE "posts"."deleted_at" IS NULL AND "posts"."topic_id" = 818063
  42323 | 2026-02-07 12:16:21.966689+00 | active | Client          | ClientRead   | SELECT "discourse_post_event_events"."id", "discourse_post_event_events"."status", "discourse_post_e
(11 rows)

La mia domanda, in sostanza, si riduce a: “cosa potrebbe creare una query UPDATE che rimane bloccata per 9 ore?”.
Ipotizzerei una quantità di memoria insufficiente: le query finiscono nello swap.
Avere una tabella posts da 40GB è un potenziale problema?


root@discourse_app:/# su - postgres -c "psql discourse -c \"SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size FROM pg_tables WHERE schemaname = 'public' ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC LIMIT 10;\""
 schemaname |     tablename     |  size
------------+-------------------+---------
 public     | posts             | 40 GB
 public     | post_search_data  | 4326 MB
 public     | topic_users       | 1306 MB
 public     | topics            | 837 MB
 public     | topic_search_data | 702 MB
 public     | post_replies      | 567 MB
 public     | top_topics        | 512 MB
 public     | user_actions      | 417 MB
 public     | topic_links       | 285 MB
 public     | directory_items   | 243 MB

Hai provato a modificare le impostazioni di memoria di postgres?

Se passare a una nuova VM risolvesse qualcosa, non sarebbe un grosso problema e potrebbe essere fatto con zero tempi di inattività e solo un po’ di tempo di sola lettura.

1 Mi Piace

Sì, l’abbiamo già fatto una volta perché avevamo un problema simile: era chiaramente Postgres che esauriva la memoria.

Questa volta i sintomi sono diversi, il che non significa che stiamo escludendo la possibilità di un problema simile, però.

Al momento molti indizi indicano che alcune strutture di thread sono troppo pesanti per le attività interne di Discourse: questa installazione deriva da una conversione fai-da-te da un forum vbb - e devo ribadire che non abbiamo avuto praticamente problemi per 2 anni consecutivi, poi l’anno scorso abbiamo dovuto modificare le impostazioni di postgres, poi tutto liscio fino a circa 10 giorni fa, e poi di nuovo dopo pochi giorni dall’ultimo aggiornamento di versione.

Il punto è che abbiamo molti thread antichi che Discourse non ha suddiviso in blocchi da 5000 post, abbiamo migliaia di account e sto iniziando a pensare che la cronologia dei post precedentemente importata + l’uso normale abbia raggiunto una soglia in cui il nostro hardware e l’architettura di Discourse faticano a gestire le operazioni normali.

Quindi, un’altra delle domande che mi pongo in questo momento è che ho notato da tempo che Discourse è un software per forum ampiamente utilizzato per grandi soluzioni commerciali (ad esempio Activision Blizzard). Capisco che si tratta di installazioni a pagamento e che il team di Discourse viene pagato per offrire un supporto adeguato, e c’è l’opzione di risolvere il problema spendendo denaro, ma non posso fare a meno di chiedermi quanto possa essere grande la loro tabella dei post, e non credo che possa essere più piccola della nostra. Tuttavia, sarei sorpreso se una soluzione self-hosted avesse problemi con l’utilizzo attivo che abbiamo (~150 utenti attivi, ~2k nuovi post al giorno, più o meno).

Inoltre, la sera in cui abbiamo messo il forum in modalità di sola lettura, il forum era velocissimo. Evidentemente, l’attività degli utenti, che include la pubblicazione ma va oltre, sta causando la situazione in cui ci troviamo.

Pertanto mi chiedo se ci sia un modo per escludere parte dei dati letti frequentemente (argomenti?) e aggiornati (statistiche utente?) in Discourse, ad esempio bloccando gli argomenti, o assegnando un livello di fiducia 0 agli utenti inattivi, o qualcosa del genere.

Questa mi sembra una buona linea di indagine: potresti avere molta RAM, ma postgres è configurato per usarla in certi modi e potrebbe crearsi delle difficoltà.

Non mi piace che ci siano due query UPDATE che sembrano identiche: sembra che possa trattarsi di un’attività pianificata che ha richiesto così tanto tempo da farne partire una seconda successivamente. Aggiungerà carico in una situazione in cui le cose non stanno già funzionando bene.

Ma non so come fare queste modifiche.

1 Mi Piace

L’ultima volta abbiamo risolto il problema che avevamo dando a Postgres più spazio: utilizzava troppi pochi processi e poca memoria di lavoro per la situazione che aveva.

Questa volta penso che abbiamo raggiunto il limite: o riduciamo il numero di processi e aumentiamo la memoria di lavoro, o viceversa, e vediamo cosa succede. Abbiamo provato la prima soluzione qualche ora fa, stiamo aspettando di vedere se sta avendo un effetto benefico.

1 Mi Piace

Sarò interessato a vedere cosa scopri. Sono stato un amministratore Linux ma non un amministratore di database. C’è un’enorme quantità di cose da sapere su entrambi i lati.

Nei documenti di postgres trovo

e

Noto che la configurazione di Discourse imposta un paio di parametri postgres in base alla dimensione della RAM. Nota che se si aumenta la dimensione del server, tali parametri non verranno modificati per la RAM più grande.

Sento che gran parte della conoscenza e della pratica potrebbero risalire al periodo in cui 2G era un server grande. È del tutto possibile che numeri più grandi siano appropriati per l’hardware odierno.

Modifica: sui miei server (da 4G) disabilito le pagine enormi (huge pages). Credo che le pagine enormi trasparenti (automatiche) possano far sì che il kernel impieghi tempo a unire e suddividere. Ma vedo nei documenti di postgres che le pagine enormi possono essere vantaggiose in alcune situazioni.

Sul mio sistema:

root@ubuntu-4gb-hel1-1:~# egrep Huge /proc/meminfo
AnonHugePages:         0 kB
ShmemHugePages:        0 kB
FileHugePages:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:               0 kB
root@ubuntu-4gb-hel1-1:~# egrep huge /proc/filesystems
nodev	hugetlbfs
root@ubuntu-4gb-hel1-1:~# 
root@ubuntu-4gb-hel1-1:~# head -v /proc/sys/vm/nr_hugepages /proc/sys/vm/nr_overcommit_hugepages
==> /proc/sys/vm/nr_hugepages <==
0


==> /proc/sys/vm/nr_overcommit_hugepages <==
0
root@ubuntu-4gb-hel1-1:~# egrep huge /sys/devices/system/node/node*/meminfo
root@ubuntu-4gb-hel1-1:~# 

A meno che le cose non siano cambiate, l’esecuzione di ./discourse-setup dovrebbe regolare i numeri ma rilevare che si tratta di un’installazione già in esecuzione. Se ricordo bene, esegue anche il backup del file app.yml prima di apportare modifiche.

Non so se questo sia cambiato di recente, dato che l’ultima volta che l’ho fatto risale a 3-4 anni fa.

Per quanto ricordo, modifica solo cose come la memoria condivisa (25% della memoria massima) e i web worker di unicorn. Potrei dimenticare qualcosa.

(non in swap, ma stanno leggendo dal disco)

questo si allinea con le mie osservazioni precedenti:

Nota che questa è la dimensione della relazione e degli indici. Confronta con pg_relation_size.

Questo proviene da ScoreCalculator, parte di PeriodicalUpdates.

Questa è la tua scoperta che deve essere risolta. A titolo di paragone, qui su meta Jobs::EnsureDbConsistency impiega <2min e Jobs::TopRefreshOlder impiega <10s:

Postgres ha bisogno di più memoria. Dagliene quanta più puoi.

Potresti anche vedere benefici da un VACUUM ANALYZE o VACUUM ANALYZE FULL. Eseguire il primo non fa mai male.

Probabilmente farei, in ordine:

  • vacuum analyze
  • metti in pausa sidekiq poi vacuum analyze full (questo congela le tabelle per riscriverle completamente, potrebbe comportare alcuni fallimenti mentre è in esecuzione)
  • più memoria a postgres
1 Mi Piace

Ho avuto un paio di ripristini/aggiornamenti falliti durante la migrazione a causa dello spazio, nonostante ci fossero ancora diversi GB di spazio libero (come Restore fails due to disk space on migration).

Eseguire un vacuum prima del backup potrebbe aiutare con quel problema?

Inoltre

Questo è

 discourse=# VACUUM FULL ANALYZE;