Newsgroup: it.comp.lang.delphi
Titolo: Sincronizzare due database / transazione unica (?)

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.

 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
 

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
 
 

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.

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

 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.

Ciao MarcoM,
 
M> Mi servirebbe il modo per realizzare passo passo un'unica transazione
M> su due db...
 
Non c'č.
 
 

> 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
 
 

Avere un unico db....?
 
A.

 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.

 Avere un unico db....?
 
Ne abbiamo già discusso, vedi mio intervento delle 10:09.
 
Grazie e ciao,
Marco M.

 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
 


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
 
 

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
 
 

 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.

 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.

 
http://tiny.cc/disconnected_rs
 
 
Grazie, lo leggerò comunque alla ricerca di possibili spunti.
 
M.

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...
 
 

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.
 
 

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
 
 

 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
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
 
iQEcBAEBAgAGBQJPIEpCAAoJED3SMOGZLYdYYKcH/2ey9gxDyWu8Rz6L8YL36pGq
dRJQMtOsrE7bJQkMHS+hsU3Yl+qO4VObcs+iWVaxiqD7sIkkBGY/wjENstC0tMI+
FV9CniVHLhDUX2Cq1YVBVVouFYhDxXWV1ewJBu8jwooJTBHPsnROc3sKxf6rexpW
dFXfKXfAsuhkYaykTRe+BIYE3TDxh/gwyXmkaFzJ2Qp0llnXgxcIDiSGWVpcQNmg
tr8gqGH9m8hCVNMtK4Ah+jQhPQAhCSmF3xCWuzNoECSBaqnppSAq8gbITZks2EYu
oWz+6cO9L5sNa8fRGtZ9tV3bmrSuPx1AdwVkyVx9llYiVtxzqDSSVk87YCDHvVg=
=BQOh
-----END PGP SIGNATURE-----
 

 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
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
 
iQEcBAEBAgAGBQJPIFBOAAoJED3SMOGZLYdYyqMIAIokUhn7z7crbZ4H9x4aJjlp
8Gr8WZQk5eQpuNqYNk6+THmfjSqH9qgyBBnB+efo3ZX3RWKYsvco3emq+KaN5lDB
FQyYYpfB1vZ7axhf+FAJMXzLb3H6DyLKeDl1rOeLNagysLmBNm2JOZ0/s2gsfkZp
Ay+TnN2foiRoSTocUNRD0gTRHOVhjm8/iL3Nv0DSrDxVEdV9yxkkG1mq7pXfF3kL
2uOpbxb9UeDtFqTAps5JCE+/8Yp0RjRERcTcwop6lHinijerhZMXefNBzk+NB3By
oRm+CRPnhcIaYyDgVKS295O6lLdkeRLxak3anFJXx2cmCZfSW9fqzurRpn8rVcE=
=u5u/
-----END PGP SIGNATURE-----
 

 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 
ab...@newsland.it
 
 
 

 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.


 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

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
 
 

 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.


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

 
....e se devi fare delle join?
 
A.

 ....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
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
 
iQEcBAEBAgAGBQJPIzb5AAoJED3SMOGZLYdYbWsH/RSCuPik/PZEc9ML7m19wGFS
iV/4znUtkYsI4v8vDuH5dqQgak6ygfhbG4g+XgI4d76dQ8oxIkERWy3VsUjkB2bb
MUBszNgu/UeFlKHGCttGVi9mqEBNaDSyTtpgAWkBwoA8KRYSjdh4w0ckWgM+iPhM
tVHV5JKA9T9Y0iU4+h5E0nETdLklv5tf7SfRkR1+aLlz1Opk5Qq/kgUmdhLoIv7Z
hmZkv3jQ5x4Y6i0KYzbIYkJZQduVMNtv5WeI4LPW9jDHqkJHpVlWyHj1YpEminos
aQH01G9iXG19EKIvLtjDIfl8td+NiIdRTXo4EMp+WCexobIR3nOCJex9Or/Fr3M=
=ibes
-----END PGP SIGNATURE-----