2015-05-21 26 views
16

La mia familiarità è con il mondo server Microsoft SQL che utilizza ADO (dbGo) e ho scritto molte applicazioni per quell'ambiente. Ora ho un'applicazione legacy Delphi 7 con un database Firebird 2.5 che devo mantenere.Come rendere l'applicazione client Firebird in attesa della riga da sbloccare

ma sto trovando è che se 2 applicazioni client esegue questo:

SQLQuery.SQL.Text := 'Update mytable set field1 = 11 where keyfield = 99' 
SQLQuery.Execute; 

a quasi esattamente nello stesso momento, il 2 ° applicazione ottiene immediatamente un errore di "stallo". In SQL Server, ci sarebbe stato un periodo di attesa

ADOConnection.Isolationlevel = ilCursorstability; 
ADOConnection.CommandTimeout := 5; 

prima di qualsiasi eccezione è sollevata nella seconda applicazione client. La gestione delle eccezioni potrebbe comportare un rollback in quella che sarebbe considerata una situazione molto insolita all'interno di un processo batch. Questo è ragionevole. 5 secondi è un tempo terribilmente lungo nei tempi di elaborazione del computer.

Ora i miei tentativi di utilizzare la stessa metodologia sul client Firebird sono stati infruttuosi perché il "deadlock" (in realtà, un record in uso) si verifica immediatamente.

Se il motore del database non può essere configurato per attendere un po 'per migliorare le condizioni (blocchi di registrazione da rilasciare), la responsabilità deve ora riposare con lo sviluppatore dell'applicazione client che deve scrivere codice follemente lento per superare ciò che sembra essere il principale fallimento di Firebird.

Una volta che è stata rilevata la "situazione di stallo", la condizione non chiara eccezione scollegando il componente di connessione

while rowsupdated = 0 and counter < 5 do 
begin 
    try 
    rowsupdated := SQLQuery.Execute; 
    except 
    SQLConnection.Connected := False; 
    SQLConnection.Connected := True; 
    end; 
    Inc(Counter) 
end; 

Come si fa a fare robuste clienti tavolo-aggiornamento multi-utente, quando non si dispone di qualsiasi sostanziale tolleranza di blocco in Firebird, usando DBX in Delphi?

+0

Non ho usato FirebirdSQL da un po ', ma ricordo che c'era una funzionalità SELECT FOR UPDATE WITH LOCK che poteva essere usata al livello SQL. Leggi questo: http://www.firebirdsql.org/refdocs/langrefupd25-notes-withlock.html – quasoft

+0

Il valore predefinito per IsolationLevel per una connessione DBExpress a Interbase è 'ReadCommitted', che è l'equivalente di' ilCursorstability'. Il 'CommandTImeout' non esiste, ma c'è' WaitOnLocks', che ha come valore predefinito 'Vero' e significa * Specifica che una transazione attende l'accesso se incontra un conflitto di blocco con un'altra transazione * (secondo i documenti). Entrambi sono impostati nei parametri della connessione. –

+0

Nonostante tutti i miei tentativi di configurare il client in modo diverso, sembra di default 'nowait'. Vedi la mia domanda a @TOndrej qui sotto. – nolaspeaker

risposta

7

Il client può specificare se la transazione deve attendere la risoluzione deadlock. Se nel tuo caso il deadlock si verifica immediatamente è probabilmente a causa della tua configurazione (usando il parametro di transazione nowait sul client). Se non si utilizza nowait, il server rileverà un deadlock e (dopo un timeout configurabile) genererà un'eccezione sul client.

Dal Firebird 2.0 è inoltre possibile specificare un timeout di blocco su una transazione dal client, annullando il valore di timeout configurato dal server.

+0

Puoi essere più specifico per favore? Come si specifica il valore di timeout di blocco sul client per un'applicazione specifica? – nolaspeaker

+0

@nolaspeaker Dipende dalla libreria client scelta e da come espone il blocco parametri transazione (TPB) che deve essere impostato a livello dell'API client Firebird. –

+0

Vedo, stai usando DBX. Quale versione/versione di Delphi? –

3

La transazione Firebird può essere configurata per essere ora in esecuzione o in attesa (con o senza un timeout specifico). Il modo in cui questo può essere configurato dipende dal driver, e dato che non ho familiarità con Delphi, non posso commentare in merito. Nowait è solitamente l'impostazione predefinita, poiché nella maggior parte dei casi l'attesa ritarderà solo l'inevitabile.

L'errore "deadlock" è un po 'improprio in quanto non è un deadlock nel normale vernacolo di concorrenza. La seconda parte dell'errore è solitamente più descrittiva, ad esempio Conflitti di aggiornamento con l'aggiornamento simultaneo., è un artefatto storico che è raggruppato in "deadlock" (anche se nella maggior parte dei casi questo errore significa che è necessario riavviare la transazione, quindi in questo senso è "morto").

0

Sto facendo uso del pulsante "rispondi alla tua domanda". Ho scoperto una soluzione.

  1. Installare IBPhoenix opensource driver ODBC per Firebird/Interbase
  2. Configurare ODBC DSN per connettersi con Firebird.fdb nowait spuntata, e LOCKTIMEOUT impostato come richiesto in secondi. Ho scelto 15 secondi.
  3. Utilizzare Delphi 7 ADO (dbGo) TADOConnection configurato per utilizzare il provider Microsoft OLE DB per i driver ODBC.
  4. Questo è il bit importante: impostare ADOConnection.TransactionIsolation su ilReadUncommited o ilDirtyRead.

Quello che fa è fa sì che il TADOQuery.ExecSQL effettivamente aspettare (per un massimo di 15 secondi specificato) qualora accerti che il record è già stato aggiornato in una transazione che non ha ancora impegnata o rotolato indietro!

Questo è diverso dal driver DBX che solleva immediatamente una cosiddetta eccezione "deadlock" in questa situazione. (Come abbiamo discusso in precedenza)

Quindi, se entrambe le query fanno

Update MYTABLE set NUM = NUM + 1 where keyvalue = 99; 

e il valore di partenza (prima di qualsiasi aggiornamento) è 0, il valore di NUM dopo che entrambe le operazioni hanno commesso è 2, come previsto .

Avvio di nuovo con NUM = 0. Se il primo rollback della transazione, la seconda transazione può eseguire il commit (o il rollback). E il valore dopo il secondo aggiornamento è stato eseguito, è solo 1.

Non so come o perché funzioni così bene, soprattutto perché Firebird non suppone supportare ReadUnComitted o DirtyRead, ma sono solo felice funziona nel modo in cui lo voglio.