2009-03-24 8 views
10

Angenommen, ich habe zwei oder mehr Prozesse, die sich mit einer SQLite-Datenbank befassen - ein "Player" -Prozess und viele "Editor" -Prozesse.Wie melde ich einen Prozess einer SQLite-Datenbankänderung in einem anderen Prozess?

Der "Player" -Prozess liest die Datenbank und aktualisiert eine Ansicht - in meinem Fall wäre es eine Wellenform, die auf der Soundkarte gemischt wird, abhängig von den in der Datenbank gespeicherten Ereignissen.

Ein "Editor" -Prozess ist jeder Editor für diese Datenbank: er ändert die Datenbank ständig.

Jetzt möchte ich der Spieler die Änderungen der Bearbeitung schnell widerspiegeln.

Ich weiß, dass SQLite Hooks liefert, um Datenbankänderungen innerhalb des gleichen Prozesses zu verfolgen, aber es scheint wenig Informationen darüber zu geben, wie dies mit mehreren Prozessen zu tun ist.

Ich konnte die Datenbank ständig abfragen, Datensätze vergleichen und Ereignisse auslösen, aber das scheint ziemlich ineffizient zu sein, besonders wenn die Datenbank zu groß wird.

Ich denke über die Verwendung einer Protokolltabelle und Trigger, aber ich frage mich, ob es eine einfachere Methode gibt.

Antwort

6

Eine relationale Datenbank ist nicht Ihre beste erste Wahl dafür.

Warum?

Sie möchten, dass alle Ihre Editoren Änderungen an Ihrem Player vornehmen.

Ihr Player ist - effektiv - ein Server für alle diese Editoren. Ihr Player benötigt mehrere offene Verbindungen. Es muss all diese Verbindungen auf Veränderungen hören. Es muss diese Änderungen anzeigen.

Wenn die Änderungen wirklich groß sind, können Sie zu einer Hybridlösung wechseln, bei der die Editoren die Änderungen und dem Spieler mitteilen.

In beiden Fällen müssen die Redakteure dem Spieler mitteilen, dass sie eine Änderung haben. Es ist viel, viel einfacher als der Spieler versucht, Änderungen in einer Datenbank zu entdecken.

Ein besseres Design ist ein Server, der Nachrichten von den Redakteuren akzeptiert, sie persistiert und den Player benachrichtigt.Dieser Server ist weder ein Editor noch ein Player, sondern lediglich ein Broker, der sicherstellt, dass alle Nachrichten behandelt werden. Es akzeptiert Verbindungen von Redakteuren und Spielern. Es verwaltet die Datenbank.

Es gibt zwei Implementierungen. Server ist der Spieler. Der Server ist vom Player getrennt. Das Design des Servers ändert sich nicht - nur das Protokoll. Wenn der Server der Player ist, ruft der Server die Player-Objekte direkt auf. Wenn der Server vom Player getrennt ist, schreibt der Server in den Socket des Players.

Wenn der Player Teil des Servers ist, werden Playerobjekte direkt aufgerufen, wenn eine Nachricht von einem Editor empfangen wird. Wenn der Player getrennt ist, sammelt ein kleiner Leser die Nachrichten von einem Sockel und ruft die Spielerobjekte an.

Der Player verbindet sich mit dem Server und wartet dann auf einen Strom von Informationen. Dies kann entweder von den Editoren oder Referenzen auf Daten, die der Server in der Datenbank gespeichert hat, eingegeben werden.

Wenn der Nachrichtenverkehr klein genug ist, damit die Netzwerklatenz kein Problem darstellt, sendet der Editor alle Daten an den Server/Player. Wenn der Nachrichtenverkehr zu groß ist, schreibt der Editor in eine Datenbank und sendet eine Nachricht mit nur einem Datenbank-FK an den Server/Player.


Bitte klären Sie "Wenn der Editor bei der Benachrichtigung abstürzt, ist der Spieler dauerhaft vermasselt" in Ihrer Frage.

Das klingt nach einem schlechten Design für den Spieler-Service. Es kann nicht "permanent vermasselt" werden, es sei denn, es wird von den verschiedenen Editoren keine Informationen erhalten. Wenn es einen Status von den Editoren erhält (aber zum Beispiel versucht, diesen Status zu spiegeln), sollten Sie ein Design in Erwägung ziehen, bei dem der Player einfach den Status vom Editor erhält und nicht "permanent durcheinander" kommen kann.

+0

Die Idee war eine stark persistente Datengestaltung, wo Änderungen nie verloren gehen. Ich stellte mir vor, dass alle Prozesse als zentraler Hub um die Datenbank herum angeordnet werden. Wenn sich die Datenbank ohne Benachrichtigung ändert, wird der Player erst aktualisiert, wenn er neu gestartet wird. Deine Idee klingt interessant. – paniq

+0

"Eine relationale Datenbank ist nicht Ihre beste erste Wahl dafür." - Was wäre dann meine beste erste Wahl? – paniq

+0

Ihre Bearbeitung kommt dem nahe, was sich aus meinem Verständnis verschiedener Beiträge hier ergeben hat. vielen Dank. – paniq

2

Öffnen Sie einfach einen Socket zwischen den beiden Prozessen und lassen Sie den Editor alle Spieler über das Update informieren.

+0

Ich dachte auch darüber nach. Aber es gibt viele Redakteure und nur einen Spieler. Nicht nur, dass sie jeweils eine Verbindung aufrechterhalten müssten (eine Art XMLRPC wäre in Ordnung, denke ich), die Benachrichtigung selbst wäre nicht zuverlässig. Wenn der Editor während der Benachrichtigung abstürzt, ist der Player permanent durcheinander. – paniq

+0

@paniq, ich denke du wirst feststellen, dass SQLite * nicht * gut mit mehreren Updates funktioniert. Es neigt dazu, die gesamte Tabelle bei jedem Update zu sperren, was schnell zu Deadlocks führen kann. –

+0

@Paul Tomblin: SQLite sperrt auf Dateiebene; das sind meist ganze Datenbanken. Dies vermeidet Deadlock, kann aber langsam sein. http://www.sqlite.org/lockingv3.html –

1

Edit: Ich nehme Prozesse auf der gleichen Maschine.

Meiner Meinung nach gibt es zwei Möglichkeiten:

  • Polling (wie Sie bereits erwähnt), aber es auf einen einzelnen Wert halten (wie eine Tabelle, die die Lastupdatetime von anderen Tabellen nur hält)

  • Verwenden Sie die dort verfügbare Interprozess-Kommunikation auf der Zielplattform. Dies können Ereignisse in Windows sein (z. B. in C# (ich weiß nicht in Python), das ManualResetEvent, AutoResetEvent oder ein Mutex, wenn Sie einen Kellner-Thread in jedem Prozess opfern möchten) oder Signals in Linux.

1

Wenn es auf der gleichen Maschine ist, wäre der einfachste Weg, benannte Pipe zu haben, "player" mit Blockierung von read() und "Editoren" ein Token in Rohr, wenn sie DB ändern.

+0

Das ist eine nette Verbesserung gegenüber meiner Idee in einer Umgebung mit mehreren Editoren und Einzelspielern. –

+0

Eigentlich könnte Ihre Idee auch in dieser Umgebung funktionieren - Spieler, der auf einem Port hört und Editor, der sich mit diesem Port verbindet, um zu benachrichtigen. Ich habe Pipe gesagt, weil es die einfachste IPC ist, die es gibt. – vartec

1

Wie viele Editorprozesse (warum Prozesse?) Und wie oft erwarten Sie Updates? Dies klingt nicht wie ein gutes Design, vor allem nicht zu berücksichtigen sqlite ist wirklich nicht zu glücklich über mehrere gleichzeitige Zugriffe auf die Datenbank.

Wenn mehrere Prozesse macht Sinn und Sie Ausdauer wollen, wäre es wahrscheinlich klüger, die Redaktion benachrichtigen Sie Ihren Player über Steckdosen, Leitungen zu haben, gemeinsam genutzten Speicher oder dergleichen, und dann den Player (auch bekannt als Server-Prozess) haben mach das Andauern.

+0

Warum Prozesse: Da all dies Python ist, verhindert die GIL, dass ich Multicore-CPUs effizient nutze. Gerade jetzt stoppt der Audio-Thread die Benutzeroberfläche. Ich möchte auch nicht, dass der Ton zusammenbricht, wenn es im Editor ein Problem gibt. – paniq

+0

Ich hätte es vorgezogen, View (Player) und persistentes Modell vollständig getrennt zu haben, aber Ihre Lösung klingt auch gut. Ich kann immer noch einen zentralen "db i/o" -Prozess haben, der Player und Editoren synchronisiert. – paniq

2

Ich denke, in diesem Fall würde ich einen Prozess zur Verwaltung der Datenbank lesen/schreiben.

Jeder Editor, der einige Änderungen an der Datenbank vornehmen möchte, ruft diesen Prozess auf, sei es über IPC oder das Netzwerk oder welche Methode auch immer.

Dieser Prozess kann den Spieler dann über eine Änderung in der Datenbank informieren. Wenn der Spieler einige Daten abrufen möchte, sollte er eine Anforderung der Daten an den Prozess stellen, der die Datenbank verwaltet. (Oder der DB-Prozess teilt ihm mit, was er benötigt, wenn er eine Änderung meldet, also keine Anforderung vom Player benötigt wird)

Dies hat den Vorteil, dass nur ein Prozess auf die SQLite-DB zugreift, also kein Sperren oder Nebenläufigkeitsprobleme in der Datenbank.

+0

Sie sind zu spät;) aber richtig, das klingt nach dem besten Ansatz. – paniq

3

SQLite hat eine update_hook Funktion, die das tut, was Sie wollen.

SQLite C-Schnittstelle

Datenänderungsmitteilung Rückrufe

void *sqlite3_update_hook(
    sqlite3*, 
    void(*)(void *,int ,char const *,char const *,sqlite3_int64), 
    void* 
); 

Die sqlite3_update_hook() Schnittstelle registriert eine Callback-Funktion mit die Verbindungsdatenbank durch das erste Argument identifiziert werden aufgerufen, wann immer Eine Zeile wird in einer Rowid-Tabelle aktualisiert, eingefügt oder gelöscht. Jeder Rückruf, der durch einen vorherigen Aufruf dieser Funktion für dieselbe Datenbank Verbindung festgelegt wurde, wird überschrieben.

Leider ist es nicht von der Python SQLite-Modul ausgesetzt ...

Hier ist eine etwas hacky Abhilfe, die die C-api gräbt sich in (von Python-Code) davon Gebrauch zu machen: https://stackoverflow.com/a/16920926

+0

Es funktioniert nur für Änderungen, die für dieselbe Datenbankverbindung ausgelöst wurden, die Sie im ersten Argument übergeben haben. Also nicht Multi-Prozess. – pcworld

+0

guter Punkt. Das passt zum sqlite-Verhalten: _Multiple Prozesse können gleichzeitig SELECT ausführen. Aber nur ein Prozess kann zu jedem Zeitpunkt Änderungen an der Datenbank vornehmen. Dies legt nahe, dass das OP einen einzigen Prozess benötigt, der die db-Verbindung hält. Die Prozesse "editor" und "player" würden dann mit diesem "db manager" -Prozess kommunizieren – Anentropic