Autor Téma: SQLite Database locked  (Přečteno 5078 krát)

Offline Andy

  • Mladík
  • **
  • Příspěvků: 51
  • Karma: 0
SQLite Database locked
« kdy: 24-09-2013, 17:30:28 »
Ahoj,
vyvijim aplikaci ktera je vyuziva SQLite jako interni datove uloziste (pro offline praci) a ve vlakne se synchronizuje z MySQL serverem.

Kdyz pustim synchronizaci "normalne" (bez vlakna - na timer) tak aplikace jede "normalne" ale kdyz se pusti synchronizace tak vymrzava aplikace (coz je dost neprijemne). Kdyz pustim synchronizaci ve vlakne, tak mi to pise Database locked.

Pro pripojeni pouzivam FireDAC (XE4)
Konektor:

Kód: [Vybrat]
  oParams := TStringList.Create;
  oParams.Add('Database=' + SYS_DATADIR + 'config.sqlite');
  oParams.Add('Pooled=True');
  oParams.Add('JournalMode=Off');
  oParams.Add('SQLiteAdvanced=page_size=4096');
  oParams.Add('CacheSize=50000');
  oParams.Add('Busytimeout=5000');
  oParams.Add('Synchronous=Off');
  oParams.Add('LockingMode=Normal');
  oParams.Add('SharedCache=False');
  ADManager.AddConnectionDef('SQLite_App', 'SQLite', oParams);
  ADManager.Active:=true;

  // pripojeni hlavni
  SQLConnection.DriverName:='SQLite';
  SQLConnection.ConnectionDefName:='SQLite_App';
  SQLConnection.Connected:=true;
  // pripojeni ve vlakne
  SQLKasa.SynchConnection.DriverName:='SQLite';
  SQLKasa.SynchConnection.ConnectionDefName:='SQLite_App';
  SQLKasa.SynchConnection.Connected:=true;

Nesetkal se nekdo z necim podobnym pripadne vedel by nekdo kterym smerem nakopnout?

Offline Mi.Chal.

  • Guru
  • *****
  • Příspěvků: 567
  • Karma: 23
Re:SQLite Database locked
« Odpověď #1 kdy: 24-09-2013, 19:34:35 »
Jak vypadá ta synchronizace ve vlákně? Nepouštíš to třeba vícekrát zároveň?

Offline pepak

  • Guru
  • *****
  • Příspěvků: 1290
  • Karma: 28
    • Pepak.net
Re:SQLite Database locked
« Odpověď #2 kdy: 24-09-2013, 20:26:47 »
Nemůže to souviset s http://www.sqlite.org/faq.html#q6?

Offline Andy

  • Mladík
  • **
  • Příspěvků: 51
  • Karma: 0
Re:SQLite Database locked
« Odpověď #3 kdy: 25-09-2013, 13:28:08 »
Pro MiChal.
Synchronizace je spoustena jen jednou a dokonce i bezi bez problemu kdyz v jednom vlakne jen ctu. Problem nastava kdyz potrebuji v obou vlaknech zapsat.
Kód: [Vybrat]
procedure TSynchronizace.Execute;
begin
  while not Terminated do begin
    SynchronizujPolozky;
  end;
end;

procedure TSynchronizace.SynchronizujPolozky;
begin
  try
    // synchronizace
  except on E:Exception do begin
    // hlaska do logu
    end;
  end;
end;

Pro pepak
Pripojeni resim 2 spojenimi jedno je vyhradne pro vlakno a druhe pro hlavni cast programu. Jak pisou snazim se ignorovat http://www.sqlite.org/faq.html#q5
DB vytvarim primo v programu a ma tyto parametry
Kód: [Vybrat]
Compile options = ENABLE_COLUMN_METADATA;ENABLE_FTS3;
  ENABLE_FTS3_PARENTHESIS;ENABLE_FTS4;ENABLE_RTREE;
  ENABLE_STAT3;HAS_CODEC;OMIT_AUTOINIT;
  OMIT_DEPRECATED;TEMP_STORE=2;THREADSAFE=2

Podle http://sqlite.org/threadsafe.html bych asi evidentne potreboval 3 variantu v SQLite nejsem zrovna moc silny jelikoz je to muj prvni projekt na SQLite.

Offline Mi.Chal.

  • Guru
  • *****
  • Příspěvků: 567
  • Karma: 23
Re:SQLite Database locked
« Odpověď #4 kdy: 25-09-2013, 17:29:57 »
jeste by to chtelo vedet, jestli mas SQLLite zbuildeny v tom thread-safe modu a jestli nemas nejaky synchronizacni problem u sebe. Connection vytvaris a pouzivas jenom v tom threadu?

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 1796
  • Karma: 77
    • Verze Delphi: D2007, XE3, DX10
Re:SQLite Database locked
« Odpověď #5 kdy: 25-09-2013, 17:40:08 »
Problem nastava kdyz potrebuji v obou vlaknech zapsat.
Z obou kterych? Z hlavniho a podruzneho?

Jak pisou snazim se ignorovat http://www.sqlite.org/faq.html#q5

Je otazkou, jestli ma smysl chc*t proti vetru...

Na tvem miste bych si napsal test, kde bych si vytvoril n threadu kazdy s vlastnim spojenim a z nich bych zapisoval a cetl do/z lokalni SQLite databaze.
Pokud to nebude fungovat, vis, ze mas problem se thread-safe zalezitostma ev. rizenim transakci a ty se daji resit.

Pokud to fungovat bude a po prehozeni DB na NFS to zacne blbnout, budes vedet, ze nemuzes ignorovat bod #5 a ze mas sahnout po  Client/Server reseni, at jiz si budes server matlat svepomoci nebo pouzijes nejakou nadstavbu nad SQlite (neco takoveho tam nekdo psal v C++) a nebo jestli sahnes po normalnim SQL serveru...

Offline Mi.Chal.

  • Guru
  • *****
  • Příspěvků: 567
  • Karma: 23
Re:SQLite Database locked
« Odpověď #6 kdy: 25-09-2013, 19:28:44 »
Je otazkou, jestli ma smysl chc*t proti vetru...

Na tvem miste bych si napsal test, kde bych si vytvoril n threadu kazdy s vlastnim spojenim a z nich bych zapisoval a cetl do/z lokalni SQLite databaze.
Pokud to nebude fungovat, vis, ze mas problem se thread-safe zalezitostma ev. rizenim transakci a ty se daji resit.

Tak oni tam psali, že zápis z více threadů neumí, takže je potřeba synchronizovat a to co tam mají v různých situacích nefunguje správně. Asi by to chtělo synchronizovat v kódu používajícím tu db.

Offline Andy

  • Mladík
  • **
  • Příspěvků: 51
  • Karma: 0
Re:SQLite Database locked
« Odpověď #7 kdy: 25-09-2013, 20:57:25 »
Tak jsem udelal mini aplikaci ktera vytvori jednu tabulku a po stisknuti "Start Thread" zapise do ni 10000 zaznamu. Kdyz v prubehu stisknu "Start Main" tak vyskoci chyba. A to je ta chyba kterou nevim jak obejit.

Zdrojovy kod mam v priloze, pripadne zkompilovany program zde http://leteckaposta.cz/489289411

Je to prene tak jak to delam v aplikaci.

Offline Mi.Chal.

  • Guru
  • *****
  • Příspěvků: 567
  • Karma: 23
Re:SQLite Database locked
« Odpověď #8 kdy: 25-09-2013, 21:38:28 »
Není divné, že otevíráš spojení v hlavním vlákně (ConnThread ve FormCreate) a pak přes něj zapisuješ v jiném?

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 1796
  • Karma: 77
    • Verze Delphi: D2007, XE3, DX10
Re:SQLite Database locked
« Odpověď #9 kdy: 25-09-2013, 22:27:23 »
Tak jsem udelal mini aplikaci ktera vytvori jednu tabulku a po stisknuti "Start Thread" zapise do ni 10000 zaznamu. Kdyz v prubehu stisknu "Start Main" tak vyskoci chyba.
Ja ten tvuj test na svem piskovisti neprelozim, tak jsem si udelal vlastni proti DEMO_SQLITE databazi z FireDac a normalne mi to funguje.
Co me na prvni pohled zarazi v tvem testu:
- v parametrech spojeni nastavujes BusyTimeout, ale UpdateOptions.LockWait mas na FALSE, takze se neuplatni a cekat to nebude
- neridis si transakce, mas to v autostart/autocommit rezimu
- pises do GUI z podruzneho threadu bez Synchronize a to myslim nejde ani s Opici

Kdyz ten tvuj test neprelozim, tak se mi nechce zkoumat, co tam mas jinak, ale moje nastaveni connectivity je nasledujici:
Kód: Delphi [Vybrat]
  1.   FDB := TADConnection.Create(nil);
  2.   FDB.Params.Add('DriverID=SQLite');
  3.   FDB.Params.Add('Database=C:\Program Files\da-soft\AnyDAC for Delphi\DB\Data\ADDemo.sdb');
  4.   FDB.Params.Add('LockingMode=normal');
  5.   FDB.Params.Add('SharedCache=false');
  6.   FDB.UpdateOptions.LockWait := True;

Takhle vypada Execute mojeho threadu:
Kód: Delphi [Vybrat]
  1.   try
  2.     FMsg := 'Before connect';
  3.     Synchronize(Log);
  4.     FDB.Connected := True;
  5.     try
  6.       FMsg := 'Before insert';
  7.       Synchronize(Log);
  8.       Txn := TADTransaction.Create(nil);
  9.       try
  10.         Txn.Connection := FDB;
  11.         Txn.Options.ReadOnly := FALSE;
  12.         Txn.Options.AutoStart := FALSE;
  13.         Txn.Options.AutoCommit := FALSE;
  14.         Txn.Options.AutoStop := FALSE;
  15.         Txn.Options.Isolation := xiReadCommitted;
  16.         FDB.Transaction := Txn;
  17.         FDB.UpdateTransaction := Txn;
  18.         Q := TADQuery.Create(nil);
  19.         try
  20.           Q.Connection := FDB;
  21.           Q.Transaction := Txn;
  22.           Txn.StartTransaction;
  23.           try
  24.             for I := 1 to 10000 do
  25.               InsertRecord(I,Q);
  26.             Txn.Commit;
  27.             FMsg := 'After commit';
  28.             Synchronize(Log);
  29.           except
  30.             Txn.Rollback;
  31.             FMsg := 'After rollback';
  32.             Synchronize(Log);
  33.             raise;
  34.           end;
  35.         finally
  36.           Q.Free;
  37.         end;
  38.       finally
  39.         Txn.Free;
  40.       end;
  41.     finally
  42.       FDB.Connected := FALSE;
  43.     end;
  44.   except on E:Exception do
  45.     begin
  46.       FMsg := Format('%s: %s',[E.ClassName, E.Message]);
  47.       Synchronize(Log);
  48.     end;
  49.   end;

A konence takhle vkladam record:
Kód: Delphi [Vybrat]
  1.   AQuery.SQL.Text := 'update sqlite_sequence set seq = seq+1 where name=:name';
  2.   AQuery.Params.ParamByName('name').AsString := 'Shippers';
  3.   AQuery.ExecSQL;
  4.   V := AQuery.Connection.ExecSQLScalar('select seq from sqlite_sequence where name=:name',['Shippers']);
  5.   //
  6.   NewName := TGUID.NewGuid.ToString;
  7.   NewPhone := Format('%s-%4d-%d',[FThreadName,AId,Random(MaxInt)]);
  8.   AQuery.SQL.Text := 'insert into Shippers (ShipperId, CompanyName, Phone) values (:id, :name, :phone)';
  9.   AQuery.Params.ParamByName('id').AsInteger := V;
  10.   AQuery.Params.ParamByName('name').AsString := NewName;
  11.   AQuery.Params.ParamByName('phone').AsString := NewPhone;
  12.   AQuery.ExecSQL;

A z GUI delam dtto.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 1796
  • Karma: 77
    • Verze Delphi: D2007, XE3, DX10
Re:SQLite Database locked
« Odpověď #10 kdy: 25-09-2013, 22:35:30 »
Není divné, že otevíráš spojení v hlavním vlákně (ConnThread ve FormCreate) a pak přes něj zapisuješ v jiném?
Divny to je, ale Hwaci v tomhle hodne vychazel vstric zizalkam, takze dneska uz by to vadit nemelo viz www.sqlite.org/sharedcache.html. Ale nezkousel jsem a v testu jsem si tradicne vytvarel vlastni spojeni, ktere mezi thready nesdilim.

Offline Mi.Chal.

  • Guru
  • *****
  • Příspěvků: 567
  • Karma: 23
Re:SQLite Database locked
« Odpověď #11 kdy: 26-09-2013, 09:43:57 »
Divny to je, ale Hwaci v tomhle hodne vychazel vstric zizalkam, takze dneska uz by to vadit nemelo viz www.sqlite.org/sharedcache.html. Ale nezkousel jsem a v testu jsem si tradicne vytvarel vlastni spojeni, ktere mezi thready nesdilim.

No nevím, podle té jejich dokumentace by se to dělat nemělo a ten přístup bude nejspíš potřeba synchronizovat.

"But only one process can be making changes to the database at any moment in time, however."

Ještě někde psali něco o tom, že tam lze přistupovat z víc threadů za předpokladu, že connection byla vyrobená (asi spíš připojená) ve stejném threadu.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 1796
  • Karma: 77
    • Verze Delphi: D2007, XE3, DX10
Re:SQLite Database locked
« Odpověď #12 kdy: 26-09-2013, 10:27:32 »
No nevím, podle té jejich dokumentace by se to dělat nemělo a ten přístup bude nejspíš potřeba synchronizovat.
"But only one process can be making changes to the database at any moment in time, however."
Ještě někde psali něco o tom, že tam lze přistupovat z víc threadů za předpokladu, že connection byla vyrobená (asi spíš připojená) ve stejném threadu.
Ano, SQLite umoznuje vicenasobne cteni a jenom jeden zapis a k vylouceni pristupu pouziva zamykani na ruzne urovni. Ale soucasne na urovni API nabizi handling stavu BUSY po dobu, kdy je neco zamknuto. Takze pokud pouzivas spravny wrapper popr. spravne s SQLite  komunikujes, tak samozrejme muzes z vice threadu pozadovat operaci zapisu  a ty pozadavky se budou s pomoci BUSY mechanismu automaticky serializovat, takze nemusis nic sam synchronizovat.

Mas pravdu v tom, ze aby se to samo synchronizovalo, musis mit connection per thread, protoze BUSY callback je jeden per connection. Pokud bys ho nechtel pouzivat a synchronizoval si to sam na zaklade vysledku SQLITE_BUSY, tak bys mohl connections sdilet vice thready jak pisou v tom clanku, na ktery jsem posilal naposledy odkaz.

 

S rychlou odpovědí můžete používat BB kódy a emotikony jako v běžném okně pro odpověď, ale daleko rychleji.

Upozornění: do tohoto tématu bylo naposledy přispěno před 120 dny.
Zvažte prosím založení nového tématu.

Jméno: E-mail:
Ověření:
Datový typ v Delphi, který má True a False: