Jak zapisywać do jednej bazy SQLite z dwóch aplikacji w tym samym czasie?

0

Cześć.

Pracuję nad dwoma aplikacjami działającymi w tym samym czasie na jednej bazie sqlite. Same aplikacje nie są skomplikowanie i stąd też wybór możliwie prostej bazy. Ale mam sytuację, że obie aplikacje powinny mieć możliwość zapisu do tej samej bazy, a nawet tabeli, dodając kolejne rekordy. Mój problem polega na tym, że tylko jedna aplikacja może w tym samym czasie zapisywać. Druga może co najwyżej czytać. Podejrzewam, że jest to jakaś kwestia synchronizacji. Obie aplikacje uruchamiane są na tym samym komputerze i obie znajdują się w tym samym katalogu. Być może czasem plik bazy wyląduje na jakimś zasobie sieciowym, a aplikacje będą na różnych komputerach...
Prosiłbym o informację, czy jest to możliwa taka praca na sqlite i jak rozwiązać ten problem.

5
amidar napisał(a):

Prosiłbym o informację, czy jest to możliwa taka praca na sqlite

W zasadzie tak (reszra poniżej)

i jak rozwiązać ten problem.

Użyć pełnowartościowej serwerowej bazy, a nie zabawki.
MS-SQL EXpres, MySQL / Maria DB / Posgress

SQLite jest bezserwerową bazą plikową, i to jest jak najgorszy wybór do dostępu dzielonego.
(Poza wątkiem, augmentów na "nie" mam wiele więcej, np pola pozbawione w rzeczywistości typu, przez co ukrywa błędy)

4

Nie ma takiej możliwości aby zapisywać do SQLite z kilku procesów: https://sqlite.org/faq.html#q5

3

Do takiego problemu:

  • albo inna baza
  • albo jakaś warstwa pośrednia
0

Tak mi się coś wydawało, że będzie tutaj problem. Cóż... chciałem uniknąć "dużej bazy" bo projekt prosty. A sqlite jest szybko przenaszalne.

4

Jak chcesz małą i prostą bazę serwerową to zainteresuj się firebirdem

0

Jednocześnie nie 2 aplikacje na pewno nie mogą zapisywać do bazy SQLite ale teoretycznie (nie testowałem) jeżeli są zapisywane jakieś pojedyncze rekordy których czas zapisu nigdy nie będzie długi to można spróbować trybu WAL / WAL2 (dla bezpiecznego jednoczesnego zapisu i odczytu) i ustawić sensowny Busy Timeout to i tak jak pisałem i koledzy wcześniej jednoczesnego zapisu na pewno by nie było ale jeżeli interesuje cię rozwiązanie w którym druga apka w przypadku gdy jedna zapisuje czekała by na możliwość zapisu przez ustawiony timeout (zamiast od razu wywalić błąd SQLITE_BUSY) więc wydaje mi się, że to mogłoby działać.

2

No to jeśli potrzebujesz z wielu aplikacji dostęp do tej samej bazy to z sqlite będzie ciężko. Możesz użyć postgresql w wersji portable lub jeszcze proście https://firebirdsql.org/ ktory technicznie jest bazą jednoplikową ale z wielodostępem i wszystkimi zaletami normalnych baz

1

Oczywiście że się da, ale raczej będzie to zakładanie gaci przez głowę. Jeśli zagwarantowałbyś, że do bazy jest podpięty zawsze jeden proces/klient, to spoko. Kwestia zrobienia jakiegoś sensownego locka (mówisz że obie aplikacje są na tym samym komputerze - to może uprościć sprawę) który nie pozwoli na dostęp więcej niż 1 procesowi, no i musisz pewnie zamykać połączenie za każdym razem, co może zepsuć wydajność. Kwestia twoich wymagań, bardzo możliwe że nie warto, ale da się.

0

Na szczęście w komponentach obsługujących bazę danych zabudowałem możliwość przełączenia się na ODBC. Więc na szybko przepnę się na inną bazę, a później zastanowię się co dalej. Pewnie konfiguracja zostanie w sqLite, a reszta powędruje do innej bazy. Może faktycznie firebird.

1

Jest jeszcze inna możliwość, jeśli chodzi o kilka aplikacji i jedną SQLite. Trzecia aplikacja, która sama będzie korzystać z bazy i będzie jej jedynym użytkownikiem, ale będzie też serwerem TCP. W dwóch apkach, które mają korzystać z bazy byłby klient TCP, który łączy się z serwerem po localhost. Sam protokół może być bardzo prosty, bo od klienta do serwera będzie się wysyłać same teksty, które serwer będzie wysyłać do bazy jako polecenie SQL, a od serwera do klienta byłaby wysyłana odpowiedź na zapytanie SELECT.

Nie wiem, czy to się da zrobić w Lazarus, ale jakiś czas temu, na własny użytek w C#/.NET zrobiłem taką prostą bibliotekę, która zawiera klasy dziedziczące po standardowych interfejsach i klasach potrzebnych do obsługi baz SQL (i to tylko w niezbędnym zakresie) i w taki sposób zrobiłem dodatkową apke pośredniczącą pomiędzy baza danych, a właściwą apką korzystajacą, bez wielkego przerabiania tej apki. Tu akurat chodziło o "nasłuch" poleceń SQL i umożliwienie połączenia przy braku bezpośrednio wystawionego adresu i portu serwera MS SQL.

Do tego celu, nie ma znaczenia, czy zrobisz klienta kompatybilnego z ODBC, czy coś prostego i swojego, bo tak naprawdę jest to podstawa robienia łącza TCP/IP, coś co jest w tutorialach na ten temat.

Teoretycznie jest jeszcze inna możliwość, tzw SQL Pooler, Connection pooler, różne to nazwy ma, ale chodzi o gotową aplikację zgodną z ODBC, która z jednej strony podłącza się do bazy danych ODBC czy innej, a z drugiej strony jest serwerem bazy danych. Od strony bazy danych jest jedno połączenie, podczas, gdy w praktyce korzysta kilka kientów jednocześnie. Pooler, kolejuje żądania klientów i wysyła sekwencyjnie do bazy danych, a służy to zapobieganiu tworzenia tysięcy logicznych połączeń między SZBD, a serwerem aplikacyjnym, gdy ów serwer ma tysiące jednoczesnych użytkowników. To, co zaproponowałem na początku, to właściwie byłby taki prymitywny pooler, tyle, że nie korzystający ze standardowych rozwiązań i zrobiony możliwie najprościej.

0
amidar napisał(a):

Na szczęście w komponentach obsługujących bazę danych zabudowałem możliwość przełączenia się na ODBC. Więc na szybko przepnę się na inną bazę, a później zastanowię się co dalej. Pewnie konfiguracja zostanie w sqLite, a reszta powędruje do innej bazy. Może faktycznie firebird.

Odradzam robienia takich potworków. Konfigurację polecam trzymać w pliku tekstowym: ini lub xml lub json lub w https://github.com/furious-programming/TreeStructInfo. A jeżeli "konfiguracja" jest tak duża, że musisz ją trzymać w bazie danych to wykorzystaj ten sam silnik bazy co w reszcie programu.

1
andrzejlisek napisał(a):

Jest jeszcze inna możliwość, jeśli chodzi o kilka aplikacji i jedną SQLite. Trzecia aplikacja, która sama będzie korzystać z bazy i będzie jej jedynym użytkownikiem, ale będzie też serwerem TCP. W dwóch apkach, które mają korzystać z bazy byłby klient TCP

Ile błedów w tym popełnisz, ile usztywnisz, ile to będzie trwało, a cała inwestycja dedykowana bardzo słabej bazie danych, właściwie to eskuelowemu notatnikowi

amidar napisał(a):

Pewnie konfiguracja zostanie w sqLite, a reszta powędruje do innej bazy.

Tak to się pisze prowizorki bez warstw abstrakcji

Firebird jest bazą niegdyś zasłużoną (była pierwszą bazą prawdziwie serwerowego SQL dostępną za frajer), ale "najlepsze lata" ma "dość dawno" za sobą.
Prawdą jest, że realizuje koncepcję jednego pliku - co jak się głębiej zastanowić nie świadczy o niczym w 95% sytuacji,. bo rzadko ilość plików na serwerze kogokolwiek obchodzi

1

OK, ale pytanie jest inne: jak często będą się te zapisy odbywały?
Bo zakładam, że jakaś prosta apka, odpalona na 2-3 kompach w biurze, nie będzie walić 500 insertów na minutę, tylko raczej czasem coś odczyta, a jeszcze rzadziej zapisze.
W związku z tym myślę (OK, jest to lekka prowizorka), że można zrobić tak, aby nie trzymać połączenia na stałe z bazą, tylko nawiązywać je w momencie, kiedy pojawi się potrzeba zapisu/odczytu, a potem je zamykać. W ten sposób nic się nie będzie blokować.
A jeśli jakimś cudem akurat w tym samym momencie będzie zapis z 2 instancji apki, to po prostu - poczekaj kilka sekund i ponów próbę zapisu.

Zresztą polecam zajrzeć do FAQ na stronie twórcy SQLite - https://www.sqlite.org/faq.html#q5

We are aware of no other embedded SQL database engine that supports as much concurrency as SQLite. SQLite allows multiple processes to have the database file open at once, and for multiple processes to read the database at once. When any process wants to write, it must lock the entire database file for the duration of its update. But that normally only takes a few milliseconds. Other processes just wait on the writer to finish then continue about their business. Other embedded SQL database engines typically only allow a single process to connect to the database at once.

0

jeśli aplikacja pod Windows to w tych najprostszych używam bazy msaccess czyli mdb. Marne to jest ale do niektórych zastosowań jak znalazł. Poza tym każdy Windows to obsługuje z marszu, bez żadnych ekstra bibliotek itp. Narzędzi do tworzenia, zarządzania jest dużo, darmowy i fajny jest MDB Viewer Plus http://www.alexnolan.net/software/mdb_viewer_plus.htm

0
amidar napisał(a):

Pracuję nad dwoma aplikacjami działającymi w tym samym czasie na jednej bazie sqlite.

Biorąc pod uwagę wszystko co do tej pory zostało w tym wątku napisane, dodaj jeszcze trzecią aplikację (serwer), która będzie pośredniczyć w transakcjach. W ten sposób możesz rozwiązać problem synchronizacji zapisu przez wiele procesów, poprzez zwyczajne kolejkowanie żądań zapisu danych. Żądania odczytu mogą być wykonywane natychmiast, ale nic nie stoi na przeszkodzie, aby i te również kolejkować — dla zachowania kolejności żądań płynących od jednego klienta.

Dla jasności — nie znam się zbytnio na bazach danych, to nie mój konik. Jednak ten problem jest podobny do przypadku, w którym mamy wielowątkową aplikację i jej wątki działają jednocześnie, wykorzystując ten sam zasób. Żeby to działało poprawnie, również należy synchronizować zapis i odczyt, na przykład za pomocą sekcji krytycznych. W twoim przypadku taką sekcją krytyczną byłby serwer i kolejka żądań.

1
furious programming napisał(a):

Biorąc pod uwagę wszystko co do tej pory zostało w tym wątku napisane, dodaj jeszcze trzecią aplikację (serwer), która będzie pośredniczyć w transakcjach. W ten sposób możesz rozwiązać problem synchronizacji zapisu przez wiele procesów, poprzez zwyczajne kolejkowanie żądań zapisu danych. Żądania odczytu mogą być wykonywane natychmiast, ale nic nie stoi na przeszkodzie, aby i te również kolejkować — dla zachowania kolejności żądań płynących od jednego klienta.

Ale taką propozycję już stanowczo odradził kolega:

AnyKtokolwiek napisał(a):

Ile błedów w tym popełnisz, ile usztywnisz, ile to będzie trwało, a cała inwestycja dedykowana bardzo słabej bazie danych, właściwie to eskuelowemu notatnikowi

Dokładnie to jest sedno problemu. Pytający widać, że nie zna się na pisaniu takiego softu, będzie wiele błędów, nie wiadomo jaka wydajność tego. Dodatkowo trzeba by jakiś własny protokół stworzyć. Za dużo roboty. O wiele lepiej będzie użyć wymienionego już wcześniej Firebirda w wersji embedded. Pełnowymiarowa baza danych z sql'em. Do tego w zestaw bardzo dobrych komponentów. Po co wymyślać koło na nowo? Przy samodzielnym pisaniu takiego pośrednika na pewno jakiś przypadek brzegowy zostanie przeoczony. W ramach nauki, czemu nie. Ale jeśli to ma służyć realnej pracy nie ma co kombinować. Tak samo jak z bazami w plikach MDB.

1

Z tego co widzę, zostało zadane pytanie:

Jak zapisywać do jednej bazy SQLite z dwóch aplikacji w tym samym czasie?

a nie:

Na który silnik zamienić SQLite, aby zapisywać z dwóch aplikacji w tym samym czasie?

Jeśli OP nie chce zmienić silnika, a koniecznie wykorzystać SQLite, to synchronizacja żądań jest jedynym poprawnym rozwiązaniem. A jeśli może wymienić silnik na inny, to już wy o tym napisaliście w poprzednich postach.

0
furious programming napisał(a):

Z tego co widzę, zostało zadane pytanie:

Racja, ale wtedy temat można by zamknąć odpowiedzi:

Paweł Dmitruk napisał(a):

Nie ma takiej możliwości aby zapisywać do SQLite z kilku procesów: https://sqlite.org/faq.html#q5

;) Chociaż pyta potem:

amidar napisał(a):

Prosiłbym o informację, czy jest to możliwa taka praca na sqlite i jak rozwiązać ten problem.

To rozwiązaniem problemu by było napisanie pośrednika do którego się łączą dwie aplikacje. Pytanie ile czasu i energii zajmie napisanie takiego rozwiązania. Osobiście uważam, że skoro sam pytający stwierdził, że aplikacje są mało skomplikowane, to stworzenie takiego pośrednika będzie trwało dłużej niż napisane samych aplikacji. A i tak pewnie coś się zacznie w pewnym momencie krzaczyć.

0
Mr.YaHooo napisał(a):

Racja, ale wtedy temat można by zamknąć odpowiedzi:

Paweł Dmitruk napisał(a):

Nie ma takiej możliwości aby zapisywać do SQLite z kilku procesów: https://sqlite.org/faq.html#q5

I dokładnie taka odpowiedź jest jedyną poprawną i pierwszorzędną, jaka powinna paść w tym wątku. W jednym zdaniu po pierwsze odpowiada na pytanie, a po drugie, jest link do oficjalnych materiałów na temat silnika, na potwierdzenie.

Jedyne czego w tej odpowiedzi zabrakło, to słowa bezpośrednio, bo silnik nie wspiera, ale nic nie stoi na przeszkodzie w implementacji warstwy pośredniej. Toż to klasyczny przypadek pokroju: wielu chce zapisywać jednocześnie, a tylko jeden może — trzeba ich ustawić w kolejce.

Osobiście uważam, że skoro sam pytający stwierdził, że aplikacje są mało skomplikowane, to stworzenie takiego pośrednika będzie trwało dłużej niż napisane samych aplikacji.

Nie ma podstaw, aby tak sądzić. Szczególnie, że Lazarus jest wypakowany wysokopoziomowymi klasami, z których można by takie rozwiązanie zbudować, pisząc garstkę linijek kodu.

A i tak pewnie coś się zacznie w pewnym momencie krzaczyć.

Więcej wiary w ludzkość.

1

Jak zdecydujesz się zostać na sqlite i pisać własną warstwę pośrednią, to nie musisz też robić tego od zera. Możesz skorzystać z gotowych komponentów, np. mORMot2 czy REST Dataware

0
furious programming napisał(a):

I dokładnie taka odpowiedź jest jedyną poprawną i pierwszorzędną, jaka powinna paść w tym wątku. W jednym zdaniu po pierwsze odpowiada na pytanie, a po drugie, jest link do oficjalnych materiałów na temat silnika, na potwierdzenie.

Cóż, taka specyfika for w internecie, że odpowiedź czasami nie jest taka jak być powinna i temat zmienia kierunek ;)

furious programming napisał(a):

Nie ma podstaw, aby tak sądzić. Szczególnie, że Lazarus jest wypakowany wysokopoziomowymi klasami, z których można by takie rozwiązanie zbudować, pisząc garstkę linijek kodu.

Nie jest to trywialne zadanie, więc nie zakładałbym tego. Szczególnie, że pewnie trzeba by użyć wątków do takich połączeń. Potem to wszystko zsynchronizować, a to czasami daje się we znaki.

furious programming napisał(a):

Więcej wiary w ludzkość.

Cóż, tu nawet nie chodzi o ludzkość, po prostu nie jeden raz miałem przypadek, że coś miało być proste, a potem okazywało się inaczej ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1