Ungewöhnlich hohe CPU-Auslastung

Eine letzte Kleinigkeit, dann glaube ich, bin ich für ein paar Stunden raus.

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)

Meine Frage läuft im Grunde auf „was könnte eine UPDATE-Abfrage verursachen, die 9 Stunden lang hängt?“ hinaus.
Ich würde vermuten, dass nicht genug Speicher vorhanden ist: Abfragen landen im Swap.
Ist eine 40 GB große posts-Tabelle ein mögliches Problem?


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

Haben Sie versucht, die PostgreSQL-Speichereinstellungen anzupassen?

Wenn ein Umzug auf eine neue VM irgendetwas beheben würde, ist das keine große Sache und kann mit null Ausfallzeit und nur einer kurzen Lesezeit durchgeführt werden.

1 „Gefällt mir“

Ja, das haben wir bereits einmal getan, weil wir ein ähnliches Problem hatten – es war eindeutig, dass PostgreSQL der Arbeitsspeicher ausging.

Dieses Mal sind die Symptome anders – das schließt jedoch nicht aus, dass wir ein ähnliches Problem haben.

Im Moment deuten viele Hinweise darauf hin, dass einige Thread-Strukturen für die internen Aufgaben von Discourse zu schwer sind: Diese Installation stammt aus einer DIY-Konvertierung eines vbb-Forums – und ich muss noch einmal betonen, dass wir zwei Jahre lang so gut wie keine Probleme hatten, dann mussten wir letztes Jahr die PostgreSQL-Einstellungen optimieren, danach lief alles reibungslos bis vor etwa 10 Tagen, und dann wieder nach ein paar Tagen seit dem letzten Versions-Upgrade.

Der Punkt ist, dass wir viele alte Threads haben, die Discourse nicht in 5000-Beitrags-Blöcke aufgeteilt hat, wir haben Tausende von Konten, und ich fange an zu glauben, dass die zuvor importierte Beitragshistorie + die normale Nutzung einen Schwellenwert erreicht haben, bei dem unsere Hardware und die Discourse-Architektur Schwierigkeiten haben, die normalen Operationen zu bewältigen.

Eine weitere Frage, die ich mir gerade stelle, ist, dass ich schon lange bemerkt habe, dass Discourse eine weit verbreitete Forensoftware für riesige kommerzielle Lösungen ist (z. B. Activision Blizzard). Ich verstehe, dass dies bezahlte Installationen sind und das Discourse-Team dafür bezahlt wird, angemessenen Support zu leisten, und es gibt die Möglichkeit, Geld für das Problem auszugeben, aber ich frage mich, wie groß deren Beitrags-Tabelle sein kann, und ich glaube nicht, dass sie kleiner sein kann als unsere. Dennoch wäre ich überrascht, wenn eine selbst gehostete Lösung Probleme mit der aktiven Nutzung hätte, die wir haben (~150 aktive Benutzer, ~2.000 neue Beiträge pro Tag, mehr oder weniger).

Außerdem war das Forum blitzschnell an dem Abend, an dem wir es auf schreibgeschützt gesetzt haben. Offensichtlich verursacht die Benutzeraktivität, die das Posten einschließt, aber darüber hinausgeht, die Situation, in der wir uns befinden.

Daher frage ich mich, ob es eine Möglichkeit gibt, einen Teil der häufig gelesenen (Themen?) und aktualisierten (Benutzerstatistiken?) Daten in Discourse auszuschließen, zum Beispiel durch das Sperren von Themen oder das Zuweisen eines Vertrauenslevels 0 zu inaktiven Benutzern oder etwas Ähnlichem.

Das scheint mir eine gute Fragestellung zu sein: Sie haben vielleicht viel RAM, aber PostgreSQL ist so konfiguriert, dass es diesen auf bestimmte Weise nutzt, und macht es sich dadurch möglicherweise unnötig schwer.

Mir gefällt nicht, dass es zwei UPDATE-Abfragen gibt, die identisch aussehen – das könnte auf eine geplante Aufgabe hindeuten, die so lange dauerte, dass eine zweite Aufgabe anschließend geplant wurde. Dies würde die Last in einer Situation erhöhen, in der die Dinge bereits nicht gut laufen.

Aber ich weiß nicht, wie man diese Anpassungen vornimmt.

1 „Gefällt mir“

Letztes Mal haben wir das Problem gelöst, indem wir Postgres mehr Speicherplatz gegeben haben – es verwendete zu wenige Prozesse und zu wenig Arbeitsspeicher für die damalige Situation.

Dieses Mal glaube ich, dass wir an die Grenze gestoßen sind – entweder wir reduzieren die Anzahl der Prozesse und erhöhen den Arbeitsspeicher, oder umgekehrt, und schauen, was passiert. Wir haben die erste Lösung vor ein paar Stunden ausprobiert und warten darauf, ob dies eine positive Auswirkung hat.

1 „Gefällt mir“

Ich bin gespannt, was Sie herausfinden. Ich war Linux-Administrator, aber kein Datenbankadministrator. Es gibt eine riesige Menge an Wissen auf beiden Seiten.

In den PostgreSQL-Dokumenten finde ich

und

Ich stelle fest, dass die Discourse-Einrichtung einige PostgreSQL-Parameter entsprechend der RAM-Größe konfiguriert. Beachten Sie, dass diese Parameter nicht für den größeren RAM angepasst werden, wenn Sie Ihren Server hochskalieren.

Ich habe das Gefühl, dass viel Wissen und Praxis aus der Zeit stammen, als 2G ein großer Server war. Es ist durchaus möglich, dass für die heutige Hardware größere Zahlen angemessen sind.

Edit: Auf meinen (4G)-Servern deaktiviere ich riesige Seiten (huge pages). Ich glaube, transparente (automatische) riesige Seiten können dazu führen, dass der Kernel Zeit mit dem Zusammenführen und Aufteilen verbringt. Aber ich sehe in den PostgreSQL-Dokumenten, dass riesige Seiten in manchen Situationen vorteilhaft sein können.

Auf meinem System:

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:~# 

Sofern sich nichts geändert hat, sollte das Ausführen von ./discourse-setup die Zahlen anpassen, aber erkennen, dass es sich um eine bereits laufende Installation handelt. Wenn ich mich recht erinnere, wird auch die Datei app.yml gesichert, bevor Änderungen vorgenommen werden.

Ich weiß nicht, ob sich dies kürzlich geändert hat, da ich es das letzte Mal vor 3-4 Jahren gemacht habe.

Soweit ich mich erinnere, ändert es nur Dinge wie den gemeinsam genutzten Speicher (25 % des maximalen Speichers) und die Unicorn-Webworker. Vielleicht vergesse ich etwas.

(nicht im Swap, aber sie lesen von der Festplatte)

Das passt zu meinen früheren Beobachtungen:

Beachten Sie, dass dies die Größe der Relation und der Indizes ist. Vergleichen Sie dies mit pg_relation_size.

Dies stammt von ScoreCalculator, einem Teil von PeriodicalUpdates.

Dies ist Ihre Feststellung, die gelöst werden muss. Im Vergleich dazu benötigt hier auf Meta Jobs::EnsureDbConsistency <2 Min. und Jobs::TopRefreshOlder <10s:

Postgres benötigt mehr Speicher. Geben Sie ihm so viel wie möglich.

Sie könnten auch von einem VACUUM ANALYZE oder VACUUM ANALYZE FULL profitieren. Das erste zu tun, schadet nie.

Ich würde wahrscheinlich in dieser Reihenfolge vorgehen:

  • vacuum analyze
  • Sidekiq pausieren und dann vacuum analyze full (dies friert die Tabellen ein, um sie vollständig neu zu schreiben, kann während der Ausführung zu einigen Fehlern führen)
  • mehr Speicher für Postgres
1 „Gefällt mir“

Ich hatte ein paar Wiederherstellungen/Upgrades, die während der Migration aufgrund von Speicherplatz fehlgeschlagen sind, obwohl noch mehrere GB Speicherplatz frei waren (wie Restore fails due to disk space on migration).

Könnte ein vacuum vor dem Backup helfen, dieses Problem zu beheben?

Auch

Das ist

 discourse=# VACUUM FULL ANALYZE;

Guter Rat dazu hier, wie ich denke: