Discussione:
Sincronizzare due database / transazione unica (?)
(troppo vecchio per rispondere)
alimarcus
2012-01-24 17:41:10 UTC
Permalink
Salve a tutti, espongo subito il mio problema.

Ho due gestionali identici che funzionano su pc distanti tra di loro, ma collegati da una vpn su adsl. Ognuno lavora sul proprio database locale (si tratta di Firebird, ma credo sia indifferente ai fini del problema).

C'è la necessità di mantenere sincronizzata una tabella, quella dei clienti, in modo che da entrambe le sedi si possano eseguire modifiche/inserimenti/eliminazioni e la tabella rimanga di fatto univoca.

Ho già creato un software esterno che effetta periodicamente l'operazione di sincronizzazione (basta una volta al giorno) sulla base del confronto tra i dati presenti nella stessa tabella dei due database e di appositi marcatori di record lasciati dai due gestionali. Ho messo in funzione tale software su uno dei pc col gestionale, quindi la sincronizzazione avviene tramite connessione ad un database locale e l'altro remoto.
La seconda connessione è ovviamente più lenta, ma il tutto funziona bene.

A questo punto mi trovo a gestire i casi di caduta di linea/rete.
Per la sincronizzazione di OGNI record, occorre eseguire modifiche su entrambi i database ed è chiaro che se la prima modifica va e buon fine ma la seconda no, allora i dati diventano inconsistenti. Bisogna quindi evitare di aggiornare da una parte sola: o da tutte e due o niente.
L'ideale sarebbe di poter disporre di un'unica transazione su entrambi i database, ma non mi risulta che esista.

Avete qualche idea?

Grazie, Marco M.
morde
2012-01-25 08:44:03 UTC
Permalink
Post by alimarcus
Avete qualche idea?
Si io ho un'idea che non ti piacerà: migra in una architettura
client/server usando un solo database ed elimini di colpo tutto il
problema del synch delle tabelle.
Sei d'accordo?

(Un conto è che non hai un'adsl, allora avrebbe senso usare un
meccanismo di synch: l'avevo implementato con i recordset disconnessi
qualche annetto fa.)
--
morde
QT 4.7
Delphi (5,6,7.. expired)
Firebird Database
MarcoM
2012-01-25 09:09:50 UTC
Permalink
Grazie del tuo intervento.

In realtà il programma ha già un'architettura client/server e gestisce correttamente la concorrenza: ce l'ho in funzione da altre parti su reti locali *vere* e non dà problemi.

Come primo passo avevo proprio provato una configurazione simile, ma c'erano due ostacoli:
1) il funzionamento del gestionale dalla sede remota tramite vpn/adsl era lentissimo;
2) in determinati orari il programma deve svolgere il suo compito, non solo velocemente, ma anche senza blocchi, e questo non è garantito a fronte di possibili cadute di linea.

Ecco perchè era venuto naturale pensare a database locali (funzionamento rapido) con sincronizzazione periodica dell'unica tabella che interessa _condividere_ (quella dei clienti).
Preciso che non serve avere una sincronizzazione continua, ma basta che l'allineamento venga compiuto una volta al giorno.
Brunello
2012-01-25 10:24:44 UTC
Permalink
visto che l'anello debole è il pc remoto, per primo tenti la
registrazione dei dati su quello poi se tutto è andato a buon fine
trascrivi il record in locale, ciao Brunello
morde
2012-01-25 13:46:37 UTC
Permalink
Ecco perchè era venuto naturale pensare a database locali (funzionamento rapido) con sincronizzazione periodica dell'unica tabella che interessa_condividere_ (quella dei clienti).
Preciso che non serve avere una sincronizzazione continua, ma basta che l'allineamento venga compiuto una volta al giorno.
Ok. Quale tecnologia usi per accedere al db? ADO? Con un Adodataset si
poteva usare i recordset disconnessi proprio per casi come il tuo.
Il client "debole" si connette al db e lancia un "applyupdates" o
similare (non ricordo come avevo implementato il codice) su quella table.

Qui trovi un esempio d'uso di recordset disconnessi con ado:
http://tiny.cc/disconnected_rs
--
morde
QT 4.7
Delphi (5,6,7.. expired)
Firebird Database
MarcoM
2012-01-25 15:41:17 UTC
Permalink
Post by morde
Ok. Quale tecnologia usi per accedere al db? ADO?
dbExpress
Post by morde
http://tiny.cc/disconnected_rs
Grazie, lo leggerò comunque alla ricerca di possibili spunti.

M.
David
2012-01-25 09:15:03 UTC
Permalink
Ciao alimarcus,

a> L'ideale sarebbe di poter disporre di un'unica transazione su
a> entrambi i database, ma non mi risulta che esista.

a> Avete qualche idea?

Non entro in merito a questioni di architettura e mi limito a rispondere
al tuo quesito; quello che chiedi non è fattibile: non puoi disporre di un
unica transazione su due RDBM diversi.

Puoi comunque crearne una per ogni database ed eseguire il commit su tutte
nel caso in cui sia andato tutto a buon fine oppure il rollback su tutte
in caso contrario. Una cosa di questo tipo:

DB1.StartTransaction;
try
DB2.StartTransaction;
try
// Eseguo il riallineamento
DB2.Commit;
except
DB2.RollBack;
raise;
end;

DB1.Commit;
except
DB1.RollBack;
raise;
end;

L'unica istruzione che potrebbe darti problemi è "DB1.Commit;"...

HTH,
David
MarcoM
2012-01-25 11:39:36 UTC
Permalink
Post by David
...
L'unica istruzione che potrebbe darti problemi è "DB1.Commit;"...
e non è poco!

Il problema di cui sto cercando soluzione sta tutto lì: l'impossibilità di avere confermate le modifiche da entrambe le parti o da nessuna delle due.

Mi servirebbe il modo per realizzare passo passo un'unica transazione su due db...


M.
David
2012-01-25 12:03:44 UTC
Permalink
Ciao MarcoM,

M> Mi servirebbe il modo per realizzare passo passo un'unica transazione
M> su due db...

Non c'è.
David
2012-01-25 12:08:45 UTC
Permalink
Ciao MarcoM,
Post by David
L'unica istruzione che potrebbe darti problemi è "DB1.Commit;"...
M> e non è poco!

E comunque non riesco a trovare una ragione valida per cui un database dovrebbe
dare errore durante la commit.

Ma magari mi sbaglio.

David
MarcoM
2012-01-25 13:36:33 UTC
Permalink
Post by David
E comunque non riesco a trovare una ragione valida per cui un database dovrebbe
dare errore durante la commit.
Ma magari mi sbaglio.
Eppure è molto facile che accada, basta che cada la connessione in quell'istante.

E' anche facile da simulare: attivi una connesione di rete, blocchi il programma prima del commit, poi scolleghi il cavo lan e fai ripartire.

M.
David
2012-01-25 14:10:02 UTC
Permalink
Ciao MarcoM,

M> Eppure è molto facile che accada, basta che cada la connessione in
M> quell'istante.

M> E' anche facile da simulare: attivi una connesione di rete, blocchi
M> il programma prima del commit, poi scolleghi il cavo lan e fai
M> ripartire.

Ne sei sicuro? Hai provato?

"E' anche facile da simulare..." a me non sembra.

Nel caso succeda quello che dici tu il comando Commit non viene recapitato
alla seconda connessione perché è caduta, ti viene sollevata una eccezione
ed il tuo programma esegue una RollBack sulla prima connessione.

In linea teorica quel caso funziona!

David
MarcoM
2012-01-25 15:36:15 UTC
Permalink
Post by David
...
Ne sei sicuro? Hai provato?
"E' anche facile da simulare..." a me non sembra.
David, stai scrivendo cose diverse da quelle a cui avevo risposto io. Ci mancherebbe che ora non venga eseguito un rollback.
Post by David
E comunque non riesco a trovare una ragione valida per cui un database dovrebbe
dare errore durante la commit.
Lo spezzone di codice che avevi proposto contiene due transazioni, con la seconda che potrebbe incontrare errori dopo che la prima è stata confermata: in tal caso il rollback verrebbe eseguito sullo sulla seconda ed i dati diventerebbero incostitenti.

M.
David
2012-01-25 16:29:19 UTC
Permalink
M> Lo spezzone di codice che avevi proposto contiene due transazioni,
M> con la seconda che potrebbe incontrare errori dopo che la prima è
M> stata confermata: in tal caso il rollback verrebbe eseguito sullo
M> sulla seconda ed i dati diventerebbero incostitenti.

Ne sei sicuro? Hai letto bene quello "spezzone di codice"? Hai provato ad
indentarlo per capire cosa fa?

L'unica istruzione che viene eseguita fuori dat try... except è DB2.Commit.

E se un RDBMS mi consente di fare insert, update e delete all'interno di
una transazione e poi "magicamente" mi da errore durante la Commit lo sostituisco
con uno più serio.

David
David
2012-01-25 14:16:50 UTC
Permalink
M> Eppure è molto facile che accada, basta che cada la connessione in
M> quell'istante.

Comunque anche se fosse come dici tu stai sbagliando: il programma di sincronizzazione
lo dovrai far girare in una delle due sedi.

Basta che la transazione interna sia quella con il database della sede remota
e non hai nessun problema nel caso in cui cada la connessione.

Se invece ti cade la connessione in LAN chiama un tecnico e fai rivedere
l'impianto perché qualcosa non va!

David
MarcoM
2012-01-25 15:38:44 UTC
Permalink
Post by David
...
Se invece ti cade la connessione in LAN chiama un tecnico e fai rivedere
l'impianto perché qualcosa non va!
Le transazioni non si usano solo perchè può cadere la rete, quindi anche in locale ci può essere bisogno di tornare indietro.

M.
David
2012-01-25 16:20:36 UTC
Permalink
M> Le transazioni non si usano solo perchè può cadere la rete, quindi
M> anche in locale ci può essere bisogno di tornare indietro.

Lasciamo perdere...
David
2012-01-25 16:24:45 UTC
Permalink
M> Le transazioni non si usano solo perchè può cadere la rete, quindi
M> anche in locale ci può essere bisogno di tornare indietro.

Spiegami una sola ragione valida per la quale un RDBMS dovrebbe dare un errore
durante una operazione di COMMIT.
morde
2012-01-26 08:54:20 UTC
Permalink
Post by David
M> Le transazioni non si usano solo perchè può cadere la rete, quindi
M> anche in locale ci può essere bisogno di tornare indietro.
In locale se sei l'unico a scrivere sul tuo db una transazione è
inutile. Tuttavia io scrivo sempre codice transazionato perchè lo stesso
codice senza modifiche è già pronto per un'architettura client server e
quindi multiutenza.
Post by David
Spiegami una sola ragione valida per la quale un RDBMS dovrebbe dare un
errore durante una operazione di COMMIT.
Perchè potrebbe essersi corrotto qualche indice nel frattempo, potrebbe
essere caduta la connessione al server, oppure il db potrebbe trovarsi
in stato inconsistente. Tutto questo in uno scenario C/S.
--
morde
QT 4.7
Delphi (5,6,7.. expired)
Firebird Database
David
2012-01-26 13:53:32 UTC
Permalink
Ciao Morde,

M> Perchè potrebbe essersi corrotto qualche indice nel frattempo,
M> potrebbe essere caduta la connessione al server, oppure il db
M> potrebbe trovarsi in stato inconsistente. Tutto questo in uno
M> scenario C/S.

Premetto che:

1) concordo con quello che hai risposto all'inizio del thread: "migra in
una architettura client/server usando un solo database ed elimini di colpo
tutto il problema del synch delle tabelle".
2) si sta chiedendo come si possa fare ad apportare, in un unica transazione,
modifiche a tabelle di database diversi che risiedono su server diversi.
Non si può...
3) non necessariamente, IMHO, i dati devono essere consistenti. Abbiamo infatti
uno scenario con 2 tabelle clienti "diverse", 2 database diversi, 2 server
diversi in due sedi diverse, e siccome le due tabelle vengono riallineate
una volta al giorno, a casa mia, i dati saranno comunque più inconsistenti
che consistenti

Quello che ho proposto è, sempre secondo me, la soluzione più simile ad una
"transazione unica" in quello scenario. Se poi la transazione interna la
utilizziamo per il server remoto, limitiamo il problema della disconnessione
e quindi i casi per i quali una commit possa fallire.

Nel peggiore dei casi comunque, se la commit fallisce, abbiamo dei dati che
sono aggiornati da una parte e non aggiornati dall'altra; ma rilanciando
la sincronizzazione dovrebbero ritornare consistenti... anche se dipende
da come è strutturato il tutto.

Magari però mi sfugge qualcosa.

David
morde
2012-01-27 09:26:11 UTC
Permalink
Post by David
Magari però mi sfugge qualcosa.
Un'idea: creare un terzo database C/S contenente solo i clienti. Quel DB
sarà quello comune.

In questo modo la synch sui clienti mette tutti d'accordo.
--
morde
QT 4.7
Delphi (5,6,7.. expired)
Firebird Database
David
2012-01-27 09:32:26 UTC
Permalink
Ciao Morde,

M> Un'idea: creare un terzo database C/S contenente solo i clienti. Quel
M> DB sarà quello comune.

Quoto
Alberto Salvati
2012-01-27 10:44:59 UTC
Permalink
....e se devi fare delle join?

A.
Enrico 'Henryx' Bianchi
2012-01-27 23:44:52 UTC
Permalink
Post by Alberto Salvati
....e se devi fare delle join?
Da quello che ho capito (a spanne, visto che non e` mai stato detto con
certezza), l'OP usa Firebird. Beh, nell'ultima release e` possibile eseguire
query cross database

Enrico

Alberto Salvati
2012-01-25 13:29:42 UTC
Permalink
Avere un unico db....?

A.
MarcoM
2012-01-25 13:38:14 UTC
Permalink
Post by Alberto Salvati
Avere un unico db....?
Ne abbiamo già discusso, vedi mio intervento delle 10:09.

Grazie e ciao,
Marco M.
Enrico 'Henryx' Bianchi
2012-01-25 18:30:19 UTC
Permalink
Post by alimarcus
Avete qualche idea?
Se la soluzione da te adottata funziona, direi che cercarne altre e`
inutile, anzi, tanto vale perfezionare il sincronizzatore e amen. Se il
problema e` la caduta di linea, l'unica e` andare per tentativi di
sincronizzazione facendo qualcosa del genere:

counter := 0;
maxcount := 5
isdone := False;

repeat
counter := counter + 1
try
DB1.Connect;
DB2.connect;
// [...]
DB1.Commit;
DB2.Commit;
isdone := True;
except
isdone := False;
until isdone = True or counter > maxcount;

if isdone = False then
begin
if counter > maxcount then
writeln('Numero tentativi connessione superato: synch fallito');
end
else
begin
// Altri controlli
end;

In alternativa, vedi se il tuo database supporta la sincronizzazione in
modalita` multimaster (e.g. PostgreSQL la implementa tramite tool esterni)
ed usa quella

Enrico
Enrico 'Henryx' Bianchi
2012-01-25 18:56:13 UTC
Permalink
Post by Enrico 'Henryx' Bianchi
Se la soluzione da te adottata funziona, direi che cercarne altre e`
inutile
Detto questo, vorrei aggiungere che stai risolvendo un problema non tuo,
ovvero, posto che non conosco la realta` lavorativa dove lavori, se sedeA e
sedeB devono comunicare tra di loro (e lo devono fare) e` compito di chi
gestisce l'infrastruttura fare in modo che ci siano le strutture per
assicurare che tali comunicazioni funzionino a dovere (per dire, va bene che
le ADSL costano poco, ma quel costo ha un suo perche` - la A non e` li per
bellezza - )

Enrico
ACSH
2012-01-26 08:12:17 UTC
Permalink
Post by Enrico 'Henryx' Bianchi
In alternativa, vedi se il tuo database supporta la sincronizzazione in
modalita` multimaster (e.g. PostgreSQL la implementa tramite tool esterni)
ed usa quella
AFAIK, anche Firebird.
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Paolo Arosio
2012-01-26 15:02:23 UTC
Permalink
Post by alimarcus
Salve a tutti, espongo subito il mio problema.
Ho due gestionali identici che funzionano su pc distanti tra di loro, ma collegati da una vpn su adsl. Ognuno lavora sul proprio database locale (si tratta di Firebird, ma credo sia indifferente ai fini del problema).
[...]
Post by alimarcus
L'ideale sarebbe di poter disporre di un'unica transazione su entrambi i database, ma non mi risulta che esista.
Hai già dato una lettura a
http://en.wikipedia.org/wiki/Two-phase_commit_protocol
e
http://www.firebirdsql.org/manual/ufb-about-features.html
http://www.firebirdsql.org/manual/gfix-transactions.html
?

Il tuo è un problema di two-phase commit.
Firebird, quando la transazione interessa più db, già attiva un
meccanismo 2-PC.
Le transazioni non committate su entrambi i DB finiscono nel limbo.
L'ultimo link riporta indicazioni su come gestire queste transazioni
finite in un'entità inesistente (secondo il papa attuale).

Saluti
Paolo Arosio
Loading...