Pertanyaan Apa aturan tentang mengakses basis data persisten secara bersamaan


Tampaknya aturan tentang akses bersamaan tidak berdokumen (di sisi Haskell) dan hanya menganggap pengembang akrab dengan backend tertentu yang digunakan. Untuk kebutuhan produksi, ini adalah asumsi yang sah, tetapi untuk pembuatan prototipe dan pengembangan yang santai, akan lebih baik jika paket persisten- * sedikit lebih mandiri.

Jadi, apa aturan yang mengatur akses bersamaan ke persisten-sqlite dan keluarga? Secara tersirat, harus ada beberapa derajat konkurensi yang diizinkan jika kita memiliki banyak koneksi, tetapi secara sepele menciptakan kumpulan koneksi tunggal dan panggilan replicateM x $ forkIO (useThePool connectionPool) memberikan kesalahan di bawah ini.

user error (SQLite3 returned ErrorBusy while attempting to perform step.)

EDIT: Beberapa contoh kode sekarang di bawah.

Dalam kode di bawah ini saya mengeluarkan 6 utas (nomor acak - aplikasi saya yang sebenarnya melakukan 3 utas). Setiap utas terus menyimpan dan mencari catatan (catatan unik dari yang diakses oleh utas lainnya, tetapi itu tidak masalah), mencetak salah satu bidang.

{-# LANGUAGE TemplateHaskell, QuasiQuotes
           , TypeFamilies, FlexibleContexts, GADTs
           , OverloadedStrings #-}
import Control.Concurrent (forkIO, threadDelay)
import Database.Persist
import Database.Persist.Sqlite hiding (get)
import Database.Persist.TH
import Control.Monad
import Control.Monad.IO.Class

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
SomeData
    myId Int
    myData Double
    MyId myId
|]

main = withSqlitePool "TEST" 40 $ \pool -> do
  runSqlPool (runMigration migrateAll) pool
  mapM_ forkIO [runSqlPool (dbThread i) pool | i <- [0..5]]
  threadDelay maxBound

dbThread :: Int -> SqlPersist IO ()
dbThread i = forever $ do
   x <- getBy (MyId i)
   insert (SomeData i (fromIntegral i))
   liftIO (print x)
   liftIO (threadDelay 100000) -- Just to calm down the CPU,
                               -- not needed for demonstrating
                               -- the problem

NB Nilai-nilai dari 40, TEST, dan semua catatan sewenang-wenang untuk contoh ini. Banyak nilai, termasuk yang lebih realistis, menyebabkan perilaku yang sama.

Juga perhatikan bahwa, meskipun mungkin jelas rusak ketika Anda menumpuk tindakan non-penghentian (via forever) di dalam transaksi DB (dimulai dengan runSqlPool), ini bukan masalah inti. Anda dapat membalikkan operasi tersebut dan membuat transaksi sewenang-wenang kecil tetapi masih berakhir dengan pengecualian periodik.

Output biasanya seperti:

$ ./so
Nothing
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorConstraint while attempting to perform step.)

32
2018-01-29 17:38


asal


Jawaban:


Hal yang perlu diperhatikan adalah bahwa SQLite memiliki masalah dengan penguncian ketika disimpan pada volume seperti NFS (vboxsf, NFS, SMB, mvfs, dll.) Pada banyak sistem yang menyebabkan SQLite memberikan kesalahan itu bahkan sebelum Anda berhasil membuka database. Volume ini dapat mengimplementasikan fcntl () kunci baca / tulis salah. ( http://www.sqlite.org/faq.html#q5 )

Dengan asumsi itu bukan masalah, itu juga layak disebut bahwa SQLite tidak benar-benar asli mendukung "koneksi" bersamaan ( http://www.sqlite.org/faq.html#q6 ) karena menggunakan kunci sistem file untuk memastikan bahwa dua penulisan tidak terjadi pada saat yang bersamaan. (Lihat bagian 3.0 dari http://www.sqlite.org/lockingv3.html)

Dengan asumsi semua ini diketahui, Anda juga dapat memeriksa versi sqlite3 mana yang telah tersedia untuk lingkungan Anda, karena beberapa perubahan pada cara di mana berbagai jenis kunci diperoleh terjadi dalam seri 3.x: http://www.sqlite.org/sharedcache.html

Edit: Beberapa informasi tambahan dari perpustakaan persist-sqlite3 This package includes a thin sqlite3 wrapper based on the direct-sqlite package, as well as the entire C library

Pembungkus 'Tipis' membuat saya memutuskan untuk melihatnya untuk melihat betapa tipisnya; melihat kode itu tidak tampak seolah-olah pembungkus gigih memiliki penjaga terhadap pernyataan ke kolam gagal kecuali penjaga yang diperlukan untuk menerjemahkan / memancarkan kesalahan dan mengganggu eksekusi, meskipun saya harus memberikan peringatan bahwa saya tidak nyaman dengan Haskell.

Tampaknya Anda harus waspada terhadap pernyataan di kolam yang gagal dan mencoba kembali, atau bahwa Anda membatasi ukuran kolam saat inisialisasi ke 1 (yang tampaknya kurang ideal.)


16
2018-02-03 15:12