Performanter Feed



  • Hallo,

    für mein aktuelles Projekt würde ich gerne einen Feed einbauen. Das heißt, dass viele Aktionen aufgezeichnet werden und auf der Startseite angezeigt werden.

    Beispiel:
    Es wurde ein Benutzer angelegt, ein Forenbeitrag geschrieben und ein anderer Benutzer gelöscht. Der Feed dazu wäre folgendes:

    12:00:00 UserX hat UserY angelegt
    12:30:00 UserY hat ein neues Thema mit dem Titel "Hallo!" angelegt
    12:33:00 UserX hat UserY gelöscht
    

    Es gibt eine ganze Reihe mehr von Aktionen die ich gerne im Feed hätten, aber die Idee dürfte klar sein.

    Mein Problem ist aber irgendwie die Speicherung.
    Meine erste Überlegung war folgende:

    Ich erstelle mir eine Tabelle namens "feeds" mit folgender Struktur:

    tb.feeds
    id | action | user | table | row_id
    

    id - fortlaufende Nummer
    action - das Kürzel für eine Aktion (z.B. "created_user", "opened_thread" oder "deleted_user")
    user - Welcher User hat die Aktion durchgeführt
    table - Welche Tabelle wurde für die Aktion herangezogen
    row_id - Welcher Eintrag in der Tabelle wurde für die Aktion herangezogen

    Aktionen hinterlassen in dieser Tabelle einen Eintrag mit entsprechenden Werten.

    Meine Probleme sind nun folgende:

    1. Ich weiß nicht ob das so gut ist. Könnte man Feed-Einträge auch anders speichern?

    2. Mein Hauptproblem ist die Ausgabe. Es ist benutzerabhängig welche Feed-Einträge man sieht und welche man nicht. Ein normaler User würde in dem Beispiel oben nur den "opened_thread"-Eintrag sehen - nicht aber die administrativen Dinge. Ein Admin würde alles sehen.
      Je mehr Aktionen, desto übler wird das Filtern.
      Ebenfalls sind pro Ausgabe mehrere Abfragen notwendig. Am Beispiel "opened_thread" müsste man in der User-Tabelle den Benutzer suchen, in der Forums-Tabelle den Thread und den Titel. Das kann teilweise sogar noch übler werden (wenn ich beispielsweise noch das Unterforum oder ähnliches angeben will muss ich noch gucken in welchem Unterforum sich der Thread befindet und den Titel dieses Forums auslesen).
      Ich dachte mir schon die "kompilierten" Ausgaben zu cachen (d.h. ein String wird gebaut (vom DB-Eintrag zu "User hat den Thread xyz erstellt") und in der Datenbank gespeichert). Problem hierbei ist: woher weiß ich wann ein Eintrag gecached ist? Das wäre ein benutzerspezifischer Cache und ich sehe keine Möglichkeit rauszufinden ob ein Eintrag bereits gecached ist oder nicht.

    Hat jemand vielleicht Anregungen hierfür?

    Danke



  • Du kannst für jede Tabelle (Thread, User, ...) ne eigene Historien-Tabelle erstellen, in der du die Aktionen mit loggst (mit Zeitstempel, Änderer usw.). Auslesen kannst du das dann einfach mittels UNION. Das Ergebnis könnte dann die Spalten "timestamp" (Änderungsdatum/-zeit) und "message" (Nachricht, z. B. "User xxx wurde von User yyy geändert") beinhalten. Optional kannst du ja noch weitere Infos ausgeben, wie z. B. die ID (Thread oder User), um direkt in den Datensatz springen zu können.

    Deinen Vorschlag für die feeds-Tabelle finde ich net gut, weil du auf row_id keinen FK setzen kannst, wodurch du schnell Inkonsistenzen bekommen kannst, wenn du mal irgendwelche falschen IDs insertest oder was gelöscht wird.



  • Ich hab die Struktur etwas geändert:

    Es gibt eine feed-Tabelle und eine feed_cache-Tabelle. In erstere wird die "rohe Aktion" eingetragen, zweiteres beinhaltet den gebauten String zu der Aktion.
    Momentan wird der Login als Feed-Eintrag eingetragen (einfach um was im Feed zu haben). Sieht in der Datenbank so aus:

    tb.feed
    id		action		user	created_at
    1		logged_in	1		27-12-2013 05 17:02:25
    
    tb.feed_cache
    id		feed_id		compiled_string
    1		1			<b>John Doe</b> logged in
    

    Der cached String wird direkt beim Eintrag in die feed-Tabelle generiert. Hauptvorteil ist einfach die Entlastung beim Anzeigen des Feeds - zudem habe ich so keine Probleme mit der Übersetzung, da ich ggf. einfach nur den Cache leeren und neu bauen lassen muss.

    Der Einwand bzgl. FK auf row_id ist berechtigt. Mir fällt aber spontan nicht ein, wie ich das umgehen kann. Wenn ich beispielsweise eine "additional_information" Spalte anhänge und dort z.B. Thread/User/Whatever eintrage, habe ich trotzdem Inkonsistenzen.

    Neben dem Problem existiert immer noch das Problem mit der berechtigungsbasierenden Auslese. Da der entstehende String zwar gespeichert wird, ist die Abfrage zum Cachen dennoch riesig 😕



  • Wieso machst du es net so wie ich geschrieben hab? Mit einer Tabelle je Aktion, die du loggen willst. Dann kannst du auch bequem nur die Sachen anzeigen, die der aktuelle Benutzer sehen darf.



  • Thread wieder ausgraben, nachdem ich grad an der neuen Feed-Lib sitze.

    Wenn ich für die verschiedenen Module einzelne Feed-Tabellen mache, habe ich durchaus einige Vorteile - mein feed_cache aber geht dabei aber unter, weil der jetzt inkonsistent ist.

    Im feed_cache sind nur die fertigen Strings der Feed-Aktionen, nachdem die Applikation mehrsprachig ist.

    Beispiel dazu:

    tb.blog_feed
    id
    user_id
    blog_post_id
    action
    

    Schreibt jetzt ein User einen Blogeintrag könnte das wie folgt aussehen:

    id = x
    user_id = y
    blog_post_id = z
    action = post_created
    

    Beim Erstellen des Eintrags wird im Feed Cache direkt der fertige String zu dem Feed-Eintrag erstellt ("User Y hat einen Blogpost (Blogposttitel) geschrieben"). So spare ich mir beim Anzeigen der Threads die ganzen Joins.

    Soll ich jetzt für jede Feed-Tabelle eine eigene Cache-Tabelle erstellen? Klingt irgendwie schrecklich.
    Oder evtl. den einzelnen Feed-Tabellen eine "meta_data"-Spalte geben, wo ein paar Informationen zu dem Eintrag sind (so kann ich mir die ganzen Joins auch sparen) - Problem wäre halt irgendwie, das "Schönste" was mir hierfür einfällt, wäre einen Array in der Datenbank serialisiert speichern - auch irgendwo unschön :|

    Ich glaube ich denke heute um einiges zu kompliziert....



  • So spare ich mir beim Anzeigen der Threads die ganzen Joins.

    Sollte Feed-Einträge, nicht Threads, heißen.


Log in to reply