diff --git a/simplexmq.cabal b/simplexmq.cabal index cb4676bed..460d8deb5 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -414,6 +414,7 @@ test-suite simplexmq-test AgentTests.EqInstances AgentTests.FunctionalAPITests AgentTests.MigrationTests + AgentTests.NotificationTests AgentTests.ServerChoice CLITests CoreTests.BatchingTests @@ -446,7 +447,6 @@ test-suite simplexmq-test Paths_simplexmq if !flag(client_postgres) other-modules: - AgentTests.NotificationTests AgentTests.SchemaDump AgentTests.SQLiteTests hs-source-dirs: diff --git a/src/Simplex/Messaging/Agent/Store.hs b/src/Simplex/Messaging/Agent/Store.hs index ff8c29b6c..1582f5cc7 100644 --- a/src/Simplex/Messaging/Agent/Store.hs +++ b/src/Simplex/Messaging/Agent/Store.hs @@ -73,6 +73,9 @@ createStore dbFilePath dbKey keepKey = Store.createDBStore dbFilePath dbKey keep closeStore :: DBStore -> IO () closeStore = Store.closeDBStore +reopenStore :: DBStore -> IO () +reopenStore = Store.reopenDBStore + execSQL :: DB.Connection -> Text -> IO [Text] execSQL = Store.execSQL diff --git a/src/Simplex/Messaging/Agent/Store/Postgres.hs b/src/Simplex/Messaging/Agent/Store/Postgres.hs index ad66c7763..4d2f6c33c 100644 --- a/src/Simplex/Messaging/Agent/Store/Postgres.hs +++ b/src/Simplex/Messaging/Agent/Store/Postgres.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} @@ -6,7 +7,8 @@ module Simplex.Messaging.Agent.Store.Postgres ( createDBStore, closeDBStore, - execSQL + reopenDBStore, + execSQL, ) where @@ -15,7 +17,7 @@ import Control.Monad (unless, void) import Data.Functor (($>)) import Data.String (fromString) import Data.Text (Text) -import Database.PostgreSQL.Simple (ConnectInfo (..), Only (..), defaultConnectInfo) +import Database.PostgreSQL.Simple (ConnectInfo (..), Only (..)) import qualified Database.PostgreSQL.Simple as PSQL import Database.PostgreSQL.Simple.SqlQQ (sql) import Simplex.Messaging.Agent.Store.Migrations (migrateSchema) @@ -24,7 +26,7 @@ import qualified Simplex.Messaging.Agent.Store.Postgres.DB as DB import Simplex.Messaging.Agent.Store.Postgres.Util (createDBAndUserIfNotExists) import Simplex.Messaging.Agent.Store.Shared (Migration (..), MigrationConfirmation (..), MigrationError (..)) import Simplex.Messaging.Util (ifM) -import UnliftIO.Exception (onException) +import UnliftIO.Exception (bracketOnError, onException) import UnliftIO.MVar import UnliftIO.STM @@ -44,11 +46,11 @@ createDBStore connectInfo schema migrations confirmMigrations = do Left e -> closeDBStore st $> Left e connectPostgresStore :: ConnectInfo -> String -> IO DBStore -connectPostgresStore dbConnectInfo schema = do - (dbConn, dbNew) <- connectDB dbConnectInfo schema -- TODO [postgres] analogue for dbBusyLoop? +connectPostgresStore dbConnectInfo dbSchema = do + (dbConn, dbNew) <- connectDB dbConnectInfo dbSchema -- TODO [postgres] analogue for dbBusyLoop? dbConnection <- newMVar dbConn dbClosed <- newTVarIO False - pure DBStore {dbConnectInfo, dbConnection, dbNew, dbClosed} + pure DBStore {dbConnectInfo, dbSchema, dbConnection, dbNew, dbClosed} connectDB :: ConnectInfo -> String -> IO (DB.Connection, Bool) connectDB dbConnectInfo schema = do @@ -81,6 +83,22 @@ closeDBStore st@DBStore {dbClosed} = DB.close conn atomically $ writeTVar dbClosed True +openPostgresStore_ :: DBStore -> IO () +openPostgresStore_ DBStore {dbConnectInfo, dbSchema, dbConnection, dbClosed} = + bracketOnError + (takeMVar dbConnection) + (tryPutMVar dbConnection) + $ \_dbConn -> do + (dbConn, _dbNew) <- connectDB dbConnectInfo dbSchema + atomically $ writeTVar dbClosed False + putMVar dbConnection dbConn + +reopenDBStore :: DBStore -> IO () +reopenDBStore st@DBStore {dbClosed} = + ifM (readTVarIO dbClosed) open (putStrLn "reopenDBStore: already opened") + where + open = openPostgresStore_ st + -- TODO [postgres] not necessary for postgres (used for ExecAgentStoreSQL, ExecChatStoreSQL) execSQL :: PSQL.Connection -> Text -> IO [Text] execSQL _db _query = throwIO (userError "not implemented") diff --git a/src/Simplex/Messaging/Agent/Store/Postgres/Common.hs b/src/Simplex/Messaging/Agent/Store/Postgres/Common.hs index b23dcf9c8..1aa73258b 100644 --- a/src/Simplex/Messaging/Agent/Store/Postgres/Common.hs +++ b/src/Simplex/Messaging/Agent/Store/Postgres/Common.hs @@ -17,6 +17,7 @@ import UnliftIO.STM -- TODO [postgres] use log_min_duration_statement instead of custom slow queries (SQLite's Connection type) data DBStore = DBStore { dbConnectInfo :: PSQL.ConnectInfo, + dbSchema :: String, dbConnection :: MVar PSQL.Connection, dbClosed :: TVar Bool, dbNew :: Bool diff --git a/src/Simplex/Messaging/Agent/Store/Postgres/Migrations/M20241210_initial.hs b/src/Simplex/Messaging/Agent/Store/Postgres/Migrations/M20241210_initial.hs index 15574d313..6b760342f 100644 --- a/src/Simplex/Messaging/Agent/Store/Postgres/Migrations/M20241210_initial.hs +++ b/src/Simplex/Messaging/Agent/Store/Postgres/Migrations/M20241210_initial.hs @@ -208,7 +208,7 @@ CREATE TABLE ntf_tokens( tkn_action BYTEA, created_at TIMESTAMPTZ NOT NULL DEFAULT (now()), updated_at TIMESTAMPTZ NOT NULL DEFAULT (now()), - ntf_mode TEXT NULL, + ntf_mode BYTEA NULL, PRIMARY KEY(provider, device_token, ntf_host, ntf_port), FOREIGN KEY(ntf_host, ntf_port) REFERENCES ntf_servers ON DELETE RESTRICT ON UPDATE CASCADE @@ -222,8 +222,8 @@ CREATE TABLE ntf_subscriptions( ntf_port TEXT NOT NULL, ntf_sub_id BYTEA, ntf_sub_status TEXT NOT NULL, - ntf_sub_action TEXT, - ntf_sub_smp_action TEXT, + ntf_sub_action BYTEA, + ntf_sub_smp_action BYTEA, ntf_sub_action_ts TIMESTAMPTZ, updated_by_supervisor SMALLINT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT (now()), diff --git a/src/Simplex/Messaging/Agent/Store/SQLite.hs b/src/Simplex/Messaging/Agent/Store/SQLite.hs index 4e03bb6f3..f023e481e 100644 --- a/src/Simplex/Messaging/Agent/Store/SQLite.hs +++ b/src/Simplex/Messaging/Agent/Store/SQLite.hs @@ -27,13 +27,12 @@ module Simplex.Messaging.Agent.Store.SQLite ( createDBStore, closeDBStore, + reopenDBStore, execSQL, -- used in Simplex.Chat.Archive sqlString, keyString, storeKey, - -- used in Simplex.Chat.Mobile and tests - reopenSQLiteStore, -- used in tests connectSQLiteStore, openSQLiteStore, @@ -127,14 +126,14 @@ openSQLiteStore_ DBStore {dbConnection, dbFilePath, dbKey, dbClosed} key keepKey writeTVar dbKey $! storeKey key keepKey putMVar dbConnection DB.Connection {conn, slow} -reopenSQLiteStore :: DBStore -> IO () -reopenSQLiteStore st@DBStore {dbKey, dbClosed} = - ifM (readTVarIO dbClosed) open (putStrLn "reopenSQLiteStore: already opened") +reopenDBStore :: DBStore -> IO () +reopenDBStore st@DBStore {dbKey, dbClosed} = + ifM (readTVarIO dbClosed) open (putStrLn "reopenDBStore: already opened") where open = readTVarIO dbKey >>= \case Just key -> openSQLiteStore_ st key True - Nothing -> fail "reopenSQLiteStore: no key" + Nothing -> fail "reopenDBStore: no key" keyString :: ScrubbedBytes -> Text keyString = sqlString . safeDecodeUtf8 . BA.convert diff --git a/tests/AgentTests.hs b/tests/AgentTests.hs index dff6cd4b0..1c0a69d8d 100644 --- a/tests/AgentTests.hs +++ b/tests/AgentTests.hs @@ -12,6 +12,7 @@ import AgentTests.ConnectionRequestTests import AgentTests.DoubleRatchetTests (doubleRatchetTests) import AgentTests.FunctionalAPITests (functionalAPITests) import AgentTests.MigrationTests (migrationTests) +import AgentTests.NotificationTests (notificationTests) import AgentTests.ServerChoice (serverChoiceTests) import Simplex.Messaging.Transport (ATransport (..)) import Test.Hspec @@ -19,7 +20,6 @@ import Test.Hspec import Fixtures import Simplex.Messaging.Agent.Store.Postgres.Util (dropAllSchemasExceptSystem) #else -import AgentTests.NotificationTests (notificationTests) import AgentTests.SQLiteTests (storeTests) #endif @@ -30,12 +30,12 @@ agentTests (ATransport t) = do describe "Double ratchet tests" doubleRatchetTests #if defined(dbPostgres) after_ (dropAllSchemasExceptSystem testDBConnectInfo) $ do +#else + do +#endif describe "Functional API" $ functionalAPITests (ATransport t) describe "Chosen servers" serverChoiceTests -#else - describe "Functional API" $ functionalAPITests (ATransport t) - describe "Chosen servers" serverChoiceTests - -- notifications aren't tested with postgres, as we don't plan to use iOS client with it - describe "Notification tests" $ notificationTests (ATransport t) + describe "Notification tests" $ notificationTests (ATransport t) +#if !defined(dbPostgres) describe "SQLite store" storeTests #endif diff --git a/tests/AgentTests/NotificationTests.hs b/tests/AgentTests/NotificationTests.hs index 33e15792e..3709c489b 100644 --- a/tests/AgentTests/NotificationTests.hs +++ b/tests/AgentTests/NotificationTests.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} @@ -53,7 +54,6 @@ import qualified Data.ByteString.Char8 as B import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as L import Data.Text.Encoding (encodeUtf8) -import Database.SQLite.Simple.QQ (sql) import NtfClient import SMPAgentClient (agentCfg, initAgentServers, initAgentServers2, testDB, testDB2, testNtfServer, testNtfServer2) import SMPClient (cfg, cfgVPrev, testPort, testPort2, testStoreLogFile2, testStoreMsgsDir2, withSmpServer, withSmpServerConfigOn, withSmpServerStoreLogOn, withSmpServerStoreMsgLogOn) @@ -62,9 +62,9 @@ import Simplex.Messaging.Agent.Client (ProtocolTestFailure (..), ProtocolTestSte import Simplex.Messaging.Agent.Env.SQLite (AgentConfig, Env (..), InitialAgentServers) import Simplex.Messaging.Agent.Protocol hiding (CON, CONF, INFO, SENT) import Simplex.Messaging.Agent.Store.AgentStore (getSavedNtfToken) -import Simplex.Messaging.Agent.Store.SQLite (closeDBStore, reopenSQLiteStore) -import Simplex.Messaging.Agent.Store.SQLite.Common (withTransaction) -import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB +import Simplex.Messaging.Agent.Store (closeStore, reopenStore) +import Simplex.Messaging.Agent.Store.Common (withTransaction) +import qualified Simplex.Messaging.Agent.Store.DB as DB import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding.String import Simplex.Messaging.Notifications.Protocol @@ -78,6 +78,11 @@ import Simplex.Messaging.Server.Env.STM (ServerConfig (..)) import Simplex.Messaging.Transport (ATransport) import Test.Hspec import UnliftIO +#if defined(dbPostgres) +import Database.PostgreSQL.Simple.SqlQQ (sql) +#else +import Database.SQLite.Simple.QQ (sql) +#endif notificationTests :: ATransport -> Spec notificationTests t = do @@ -496,7 +501,7 @@ testNotificationSubscriptionExistingConnection apns baseId alice@AgentClient {ag threadDelay 500000 suspendAgent alice 0 - closeDBStore store + closeStore store threadDelay 1000000 putStrLn "before opening the database from another agent" @@ -507,7 +512,7 @@ testNotificationSubscriptionExistingConnection apns baseId alice@AgentClient {ag threadDelay 1000000 putStrLn "after closing the database in another agent" - reopenSQLiteStore store + reopenStore store foregroundAgent alice threadDelay 500000 diff --git a/tests/AgentTests/SQLiteTests.hs b/tests/AgentTests/SQLiteTests.hs index 51112c426..d076a2fbc 100644 --- a/tests/AgentTests/SQLiteTests.hs +++ b/tests/AgentTests/SQLiteTests.hs @@ -604,7 +604,7 @@ testCloseReopenStore = do hasMigrations st closeDBStore st errorGettingMigrations st - reopenSQLiteStore st + reopenDBStore st hasMigrations st testCloseReopenEncryptedStore :: IO () @@ -615,13 +615,13 @@ testCloseReopenEncryptedStore = do closeDBStore st closeDBStore st errorGettingMigrations st - reopenSQLiteStore st `shouldThrow` \(e :: SomeException) -> "reopenSQLiteStore: no key" `isInfixOf` show e + reopenDBStore st `shouldThrow` \(e :: SomeException) -> "reopenDBStore: no key" `isInfixOf` show e openSQLiteStore st key True openSQLiteStore st key True hasMigrations st closeDBStore st errorGettingMigrations st - reopenSQLiteStore st + reopenDBStore st hasMigrations st testReopenEncryptedStoreKeepKey :: IO () @@ -631,7 +631,7 @@ testReopenEncryptedStoreKeepKey = do hasMigrations st closeDBStore st errorGettingMigrations st - reopenSQLiteStore st + reopenDBStore st hasMigrations st getMigrations :: DBStore -> IO Bool