@@ -1,4 +1,4 @@
1−{-# LANGUAGE DeriveAnyClass, DeriveGeneric #-}
1+{-# LANGUAGE DeriveAnyClass, DeriveGeneric, ScopedTypeVariables #-}
2module ProjectM36.Client
2module ProjectM36.Client
3 (ConnectionInfo(..),
3 (ConnectionInfo(..),
4 Connection(..),
4 Connection(..),
@@ -76,7 +76,7 @@ import Data.UUID.V4 (nextRandom)
76import Control.Concurrent.STM
76import Control.Concurrent.STM
77import Data.Word
77import Data.Word
78import Control.Distributed.Process (ProcessId, Process, receiveWait, send, match)
78import Control.Distributed.Process (ProcessId, Process, receiveWait, send, match)
79−import Control.Exception (IOException)
79+import Control.Exception (IOException, SomeException(..), handle)
80import Control.Concurrent.MVar
80import Control.Concurrent.MVar
81import qualified Data.Map as M
81import qualified Data.Map as M
82import Control.Distributed.Process.Serializable (Serializable)
82import Control.Distributed.Process.Serializable (Serializable)
@@ -88,6 +88,7 @@ import ProjectM36.Sessions
88import ListT
88import ListT
89import Data.Binary (Binary)
89import Data.Binary (Binary)
90import GHC.Generics (Generic)
90import GHC.Generics (Generic)
91+import Control.DeepSeq (force)
91--import Debug.Trace
92--import Debug.Trace
93type Hostname = String
94type Hostname = String
@@ -206,7 +207,7 @@ connectProjectM36 (RemoteProcessConnectionInfo databaseName serverNodeId notific
206 eLocalNode <- commonLocalNode
207 eLocalNode <- commonLocalNode
207 notificationListenerPid <- startNotificationListener notificationCallback
208 notificationListenerPid <- startNotificationListener notificationCallback
208 let dbName = remoteDBLookupName databaseName
209 let dbName = remoteDBLookupName databaseName
209− putStrLn $ show serverNodeId ++ " " ++ dbName
210+ --putStrLn $ show serverNodeId ++ " " ++ dbName
210 case eLocalNode of
211 case eLocalNode of
211 Left err -> pure (Left err)
212 Left err -> pure (Left err)
212 Right localNode -> do
213 Right localNode -> do
@@ -301,6 +302,19 @@ close (RemoteProcessConnection localNode serverProcessId) = do
301 runProcessResult localNode $ do
302 runProcessResult localNode $ do
302 call serverProcessId Logout
303 call serverProcessId Logout
305+--within the database server, we must catch and handle all exception lest they take down the database process- this handling might be different for other use-cases
306+--exceptions should generally *NOT* be thrown from any Project:M36 code paths, but third-party code such as AtomFunction scripts could conceivably throw undefined, etc.
308+excMaybe :: IO (Maybe RelationalError) -> IO (Maybe RelationalError)
309+excMaybe m = handle handler m
311+ handler (exc :: SomeException) = pure (Just (UnhandledExceptionError (show exc)))
313+excEither :: IO (Either RelationalError a) -> IO (Either RelationalError a)
314+excEither m = handle handler m
316+ handler (exc :: SomeException) = pure (Left (UnhandledExceptionError (show exc)))
304runProcessResult :: LocalNode -> Process a -> IO a
318runProcessResult :: LocalNode -> Process a -> IO a
305runProcessResult localNode proc = do
319runProcessResult localNode proc = do
306 ret <- newEmptyMVar
320 ret <- newEmptyMVar
@@ -324,32 +338,36 @@ sessionForSessionId sessionId sessions = do
325-- | Execute a relational expression in the context of the session and connection. Relational expressions are queries and therefore cannot alter the database.
339-- | Execute a relational expression in the context of the session and connection. Relational expressions are queries and therefore cannot alter the database.
326executeRelationalExpr :: SessionId -> Connection -> RelationalExpr -> IO (Either RelationalError Relation)
340executeRelationalExpr :: SessionId -> Connection -> RelationalExpr -> IO (Either RelationalError Relation)
327−executeRelationalExpr sessionId (InProcessConnection _ _ sessions _ _) expr = atomically $ do
341+executeRelationalExpr sessionId (InProcessConnection _ _ sessions _ _) expr = excEither $ atomically $ do
328 eSession <- sessionForSessionId sessionId sessions
342 eSession <- sessionForSessionId sessionId sessions
329 case eSession of
343 case eSession of
330 Left err -> pure $ Left err
344 Left err -> pure $ Left err
331− Right (Session (DisconnectedTransaction _ context)) -> pure $ evalState (RE.evalRelationalExpr expr) context
345+ Right (Session (DisconnectedTransaction _ context)) -> case evalState (RE.evalRelationalExpr expr) context of
346+ Left err -> pure (Left err)
347+ Right rel -> pure (force (Right rel)) -- this is necessary so that any undefined/error exceptions are spit out here
332executeRelationalExpr sessionId conn@(RemoteProcessConnection _ _) relExpr = remoteCall conn (ExecuteRelationalExpr sessionId relExpr)
348executeRelationalExpr sessionId conn@(RemoteProcessConnection _ _) relExpr = remoteCall conn (ExecuteRelationalExpr sessionId relExpr)
334-- | Execute a database context expression in the context of the session and connection. Database expressions modify the current session's disconnected transaction but cannot modify the transaction graph.
350-- | Execute a database context expression in the context of the session and connection. Database expressions modify the current session's disconnected transaction but cannot modify the transaction graph.
335executeDatabaseContextExpr :: SessionId -> Connection -> DatabaseContextExpr -> IO (Maybe RelationalError)
351executeDatabaseContextExpr :: SessionId -> Connection -> DatabaseContextExpr -> IO (Maybe RelationalError)
336−executeDatabaseContextExpr sessionId (InProcessConnection _ _ sessions _ _) expr = atomically $ do
352+executeDatabaseContextExpr sessionId (InProcessConnection _ _ sessions _ _) expr = excMaybe $ atomically $ do
337 eSession <- sessionForSessionId sessionId sessions
353 eSession <- sessionForSessionId sessionId sessions
338 case eSession of
354 case eSession of
339 Left err -> pure $ Just err
355 Left err -> pure $ Just err
340− Right session -> case runState (RE.evalContextExpr expr) (sessionContext session) of
357+ case runState (RE.evalContextExpr expr) (sessionContext session) of
341− (Just err,_) -> return $ Just err
358+ (Just err,_) -> return $ Just err
342− (Nothing, context') -> do
359+ (Nothing, context') -> do
343− let newDiscon = DisconnectedTransaction (sessionParentId session) context'
360+ let newDiscon = DisconnectedTransaction (sessionParentId session) context'
344− newSession = Session newDiscon
361+ newSession = Session newDiscon
345− STMMap.insert newSession sessionId sessions
362+ STMMap.insert newSession sessionId sessions
346− return Nothing
363+ pure Nothing
347executeDatabaseContextExpr sessionId conn@(RemoteProcessConnection _ _) dbExpr = remoteCall conn (ExecuteDatabaseContextExpr sessionId dbExpr)
365executeDatabaseContextExpr sessionId conn@(RemoteProcessConnection _ _) dbExpr = remoteCall conn (ExecuteDatabaseContextExpr sessionId dbExpr)
349-- | Execute a database context IO-monad-based expression for the given session and connection. `DatabaseContextIOExpr` modify the DatabaseContext but cannot be purely implemented.
367-- | Execute a database context IO-monad-based expression for the given session and connection. `DatabaseContextIOExpr` modify the DatabaseContext but cannot be purely implemented.
350--this is almost completely identical to executeDatabaseContextExpr above
368--this is almost completely identical to executeDatabaseContextExpr above
351executeDatabaseContextIOExpr :: SessionId -> Connection -> DatabaseContextIOExpr -> IO (Maybe RelationalError)
369executeDatabaseContextIOExpr :: SessionId -> Connection -> DatabaseContextIOExpr -> IO (Maybe RelationalError)
352−executeDatabaseContextIOExpr sessionId (InProcessConnection _ _ sessions _ scriptSession) expr = do
370+executeDatabaseContextIOExpr sessionId (InProcessConnection _ _ sessions _ scriptSession) expr = excMaybe $ do
353 eSession <- atomically $ sessionForSessionId sessionId sessions --potentially race condition due to interleaved IO?
371 eSession <- atomically $ sessionForSessionId sessionId sessions --potentially race condition due to interleaved IO?
354 case eSession of
372 case eSession of
355 Left err -> pure $ Just err
373 Left err -> pure $ Just err
@@ -390,7 +408,7 @@ executeCommitExprSTM_ oldContext newContext nodes = do
391-- | Execute a transaction graph expression in the context of the session and connection. Transaction graph operators modify the transaction graph state.
409-- | Execute a transaction graph expression in the context of the session and connection. Transaction graph operators modify the transaction graph state.
392executeGraphExpr :: SessionId -> Connection -> TransactionGraphOperator -> IO (Maybe RelationalError)
410executeGraphExpr :: SessionId -> Connection -> TransactionGraphOperator -> IO (Maybe RelationalError)
393−executeGraphExpr sessionId (InProcessConnection strat clientNodes sessions graphTvar _) graphExpr = do
411+executeGraphExpr sessionId (InProcessConnection strat clientNodes sessions graphTvar _) graphExpr = excMaybe $ do
394 freshId <- nextRandom
412 freshId <- nextRandom
395 manip <- atomically $ do
413 manip <- atomically $ do
396 eSession <- sessionForSessionId sessionId sessions
414 eSession <- sessionForSessionId sessionId sessions