Wenn Sie mit der Discourse Rails-Anwendung arbeiten, sei es beim Erstellen eines Plugins oder beim Erstellen eines Pull Requests für discourse/discourse, werden Sie in verschiedenen Kontexten auf N+1-Abfrageprobleme stoßen. Dieses Thema erklärt, wie Datenvorabladung behandelt wird, um solche Probleme zu beheben und Discourse leistungsfähig zu halten.
N+1-Abfrageprobleme in Rails
Wenn Sie mit N+1-Abfrageproblemen und deren Behebung in Rails noch nicht vertraut sind, lesen Sie zunächst diesen Abschnitt der Anleitungen.
Themen
Die N+1-Probleme, auf die Sie beim Laden eines Themas achten sollten, treten auf, wenn Sie Tabellen laden, die mit einem Beitrag verknüpft sind. Die Daten, die für ein einzelnes Thema serialisiert werden, werden in der Klasse TopicView vorbereitet und vorab geladen. Diese Klasse verfügt über einen on_preload-Hook, mit dem Sie zugehörige Tabellen vorab laden können, bevor die primäre Abfrage ausgeführt wird. Ein gutes Beispiel für die Verwendung von TopicView.on_preload finden Sie im ActivityPub-Plugin
Diese Verwendung des Hooks lädt sowohl benutzerdefinierte Felder, die wir unten behandeln werden, als auch zusätzliche Zuordnungen für die Beiträge im Thema vor. Sie werden feststellen, dass ActiveRecord::Associations::Preloader dafür verwendet wird. Sie können diese Klasse in der Dokumentation lesen:
Themenlisten
Die N+1-Probleme, auf die Sie beim Laden einer Themenliste achten sollten, treten auf, wenn Tabellen geladen werden, die mit einem Thema verknüpft sind. Ähnlich wie bei der Klasse TopicView für einzelne Themen gibt es für Themenlisten eine Klasse TopicList. Und genau wie TopicView verfügt auch TopicList über eine on_preload-Methode, mit der Sie sich in die Hauptabfrage der Themenliste einklinken können, um zugehörige Tabellen zu laden, bevor die Abfrage die Datenbank erreicht. Ein gutes Beispiel dafür finden Sie im Discourse Assign Plugin:
register_topic_preloader_associations
Die Klasse TopicList verfügt über eine Server-Plugin-API-Methode register_topic_preloader_associations, die im Wesentlichen das gleiche Muster wie im ActivityPub-Plugin anwendet und ActiveRecord::Associations::Preloader verwendet. Sie erledigt diese Arbeit für Sie, wenn Sie ihr ein Array von Zuordnungen übergeben.
register_topic_list_preload_user_ids
Zusätzlich zur Ausführung einer großen Abfrage, um alle benötigten Themen abzurufen, führt die Klasse TopicList auch eine große Abfrage aus, um alle für die Liste benötigten Benutzer abzurufen, einschließlich jedes:
- Thema-Benutzer
- Benutzer des letzten Beitrags
- Ausgewählter Thema-Benutzer
- Erlaubte Thema-Benutzer
Wenn Sie andere Benutzer für jedes Thema laden, können Sie mit dieser API-Methode deren Benutzer-IDs in die große Benutzerabfrage aufnehmen, damit deren Daten zusammen mit dem Rest vorab geladen werden.
Kategorien
Es gibt eine Hauptliste von Kategorien, die an verschiedenen Stellen der Website verwendet und im Site-Modell geladen und zwischengespeichert wird. Obwohl dies technisch gesehen keine Vorabladung im strengen Sinne ist, tut es etwas Ähnliches, daher habe ich es hier aufgenommen.
site_all_categories_cache_query modifier
Die Methode all_categories_cache verfügt über einen Server-Plugin-API-Modifier, mit dem Sie die große Kategorienabfrage ändern können: site_all_categories_cache_query. Sie verwenden diesen Modifier wie folgt in Ihrem plugin.rb:
register_modifier(:site_all_categories_cache_query) do |query|
query.where("categories.name LIKE 'Cool%'")
end
Suche
Auch die Vorabladung der Suche wird im Code technisch nicht so beschrieben, aber sie tut mit ihren Abfragen etwas Ähnliches, d. h. Eager Loading.
register_search_topic_eager_load
Die Server-Plugin-API-Methode register_search_topic_eager_load ermöglicht es Ihnen, zusätzliche Tabellen in der Suche vorab zu laden. Zum Beispiel:
register_search_topic_eager_load do |opts|
%i(example_table)
end
Benutzerdefinierte Felder
Benutzerdefinierte Felder werden durch das Concern HasCustomFields unterstützt. Dieses Concern wird in die Modelle aufgenommen, die zugehörige benutzerdefinierte Felder haben, d. h. Post, Topic, Category und Group. Das Modul HasCustomFields verfügt über ein eigenes Vorabladungssystem, das auf verschiedene Weise verwendet werden kann. Der beste Weg, die Vorabladung von HasCustomFields zu nutzen, sind die Server-Plugin-API-Methoden, die angesichts allem, was ich bisher gesagt habe, relativ selbsterklärend sein sollten.
register_preloaded_category_custom_fields
add_preloaded_group_custom_field
add_preloaded_topic_list_custom_field
Sie können auch einige der Low-Level-Methoden in HasCustomFields verwenden, um Ihre eigene Vorabladung bei Bedarf durchzuführen, wie wir im Beispiel des ActivityPub-Plugins gesehen haben. In diesem Beispiel wird preload_custom_fields des HasCustomFields-Concerns verwendet, um benutzerdefinierte Post-Felder in TopicView.on_preload vorab zu laden.
TopicView.on_preload do |topic_view|
##
Post.preload_custom_fields(topic_view.posts, activity_pub_post_custom_field_names)
##
end