@@ -1,4 +1,4 @@
1−{-# LANGUAGE OverloadedStrings #-}
1+{-# LANGUAGE OverloadedStrings, DeriveAnyClass, DeriveGeneric #-}
22module ProjectM36.Client
33 (ConnectionInfo(..),
44 Connection(..),
@@ -34,12 +34,16 @@ module ProjectM36.Client
3434 createSessionAtCommit,
3535 createSessionAtHead,
3636 closeSession,
37+ addClientNode,
3738 TransactionGraphOperator(..),
3839 Atomable,
3940 NodeId(..),
4041 Atom(..),
4142 Session,
4243 SessionId,
44+ NotificationCallback,
45+ emptyNotificationCallback,
46+ EvaluatedNotification(..),
4347 AtomType(..)) where
4448import ProjectM36.Base hiding (inclusionDependencies) --defined in this module as well
4549import qualified ProjectM36.Base as B
@@ -51,9 +55,10 @@ import ProjectM36.TransactionGraph
5155import ProjectM36.TransactionGraph.Persist
5256import ProjectM36.Attribute
5357import ProjectM36.Persist (DiskSync(..))
58+import ProjectM36.Notifications
5459import ProjectM36.Server.RemoteCallTypes
5560import Network.Transport.TCP (createTransport, defaultTCPParameters, encodeEndPointAddress)
56−import Control.Distributed.Process.Node (newLocalNode, initRemoteTable, runProcess, LocalNode)
61+import Control.Distributed.Process.Node (newLocalNode, initRemoteTable, runProcess, LocalNode, forkProcess)
5762import Control.Distributed.Process.Extras.Internal.Types (whereisRemote)
5863import Control.Distributed.Process.ManagedProcess.Client (call, safeCall)
5964import Control.Distributed.Process (NodeId(..))
@@ -62,16 +67,20 @@ import Data.UUID (UUID)
6267import Data.UUID.V4 (nextRandom)
6368import Control.Concurrent.STM
6469import Data.Word
65−import Control.Distributed.Process (ProcessId, Process)
70+import Control.Distributed.Process (ProcessId, Process, receiveWait, send, match)
6671import Control.Exception (IOException)
6772import Control.Concurrent.MVar
6873import qualified Data.Map as M
6974import Control.Distributed.Process.Serializable (Serializable)
7075--import Control.Distributed.Process.Debug
7176import qualified STMContainers.Map as STMMap
77+import qualified STMContainers.Set as STMSet
7278import ProjectM36.Session
7379import ProjectM36.Sessions
7480import ListT
81+import Data.Binary (Binary)
82+import GHC.Generics (Generic)
83+--import Debug.Trace
7584
7685type Hostname = String
7786
@@ -79,8 +88,29 @@ type Port = Word16
7988
8089type DatabaseName = String
8190
82−data ConnectionInfo = InProcessConnectionInfo PersistenceStrategy |
83− RemoteProcessConnectionInfo DatabaseName NodeId
91+-- | The type for notifications callbacks in the client. When a registered notification fires due to a changed relational expression evaluation, the server propagates the notifications to the clients in the form of the callback.
92+type NotificationCallback = NotificationName -> EvaluatedNotification -> IO ()
93+
94+-- | The empty notification callback ignores all callbacks.
95+emptyNotificationCallback :: NotificationCallback
96+emptyNotificationCallback _ _ = pure ()
97+
98+-- | Construct a 'ConnectionInfo' to describe how to make the 'Connection'.
99+data ConnectionInfo = InProcessConnectionInfo PersistenceStrategy NotificationCallback |
100+ RemoteProcessConnectionInfo DatabaseName NodeId NotificationCallback
101+
102+type EvaluatedNotifications = M.Map NotificationName EvaluatedNotification
103+
104+-- | Used for callbacks from the server when monitored changes have been made.
105+data NotificationMessage = NotificationMessage EvaluatedNotifications
106+ deriving (Binary, Eq, Show, Generic)
107+
108+-- | When a notification is fired, the 'reportExpr' is evaluated in the commit's context, so that is returned along with the original notification.
109+data EvaluatedNotification = EvaluatedNotification {
110+ notification :: Notification,
111+ reportRelation :: Either RelationalError Relation
112+ }
113+ deriving(Binary, Eq, Show, Generic)
84114
85115createNodeId :: Hostname -> Port -> NodeId
86116createNodeId host port = NodeId $ encodeEndPointAddress host (show port) 0
@@ -95,10 +125,12 @@ defaultHeadName :: HeadName
95125defaultHeadName = "master"
96126
97127defaultRemoteConnectionInfo :: ConnectionInfo
98−defaultRemoteConnectionInfo = RemoteProcessConnectionInfo defaultDatabaseName (createNodeId "127.0.0.1" defaultServerPort)
128+defaultRemoteConnectionInfo = RemoteProcessConnectionInfo defaultDatabaseName (createNodeId "127.0.0.1" defaultServerPort) emptyNotificationCallback
99129
100130-- | The 'Connection' represents either local or remote access to a database. All operations flow through the connection.
101−data Connection = InProcessConnection PersistenceStrategy Sessions (TVar TransactionGraph) |
131+type ClientNodes = STMSet.Set ProcessId
132+
133+data Connection = InProcessConnection PersistenceStrategy ClientNodes Sessions (TVar TransactionGraph) |
102134 RemoteProcessConnection LocalNode ProcessId
103135
104136-- | There are several reasons why a connection can fail.
@@ -118,11 +150,32 @@ commonLocalNode = do
118150 case eLocalTransport of
119151 Left err -> pure (Left $ IOExceptionError err)
120152 Right localTransport -> newLocalNode localTransport initRemoteTable >>= pure . Right
153+
154+notificationListener :: NotificationCallback -> Process ()
155+notificationListener callback = do
156+ --pid <- getSelfPid
157+ --liftIO $ putStrLn $ "LISTENER THREAD START " ++ show pid
158+ _ <- forever $ do
159+ receiveWait [
160+ match (\(NotificationMessage eNots) -> do
161+ --say $ "NOTIFICATION: " ++ show eNots
162+ liftIO $ mapM_ (uncurry callback) (M.toList eNots)
163+ )
164+ ]
165+ --say "LISTENER THREAD EXIT"
166+ pure ()
167+
168+startNotificationListener :: NotificationCallback -> IO (ProcessId)
169+startNotificationListener callback = do
170+ eLocalNode <- commonLocalNode
171+ case eLocalNode of
172+ Left err -> error ("Failed to start local notification listener: " ++ show err)
173+ Right localNode -> forkProcess localNode (notificationListener callback)
121174
122175-- | To create a 'Connection' to a remote or local database, create a connectionInfo and call 'connectProjectM36'.
123176connectProjectM36 :: ConnectionInfo -> IO (Either ConnectionError Connection)
124177--create a new in-memory database/transaction graph
125−connectProjectM36 (InProcessConnectionInfo strat) = do
178+connectProjectM36 (InProcessConnectionInfo strat notificationCallback) = do
126179 freshUUID <- nextRandom
127180 let bootstrapContext = RE.basicDatabaseContext
128181 freshGraph = bootstrapTransactionGraph freshUUID bootstrapContext
@@ -130,14 +183,19 @@ connectProjectM36 (InProcessConnectionInfo strat) = do
130183 --create date examples graph for now- probably should be empty context in the future
131184 NoPersistence -> do
132185 graphTvar <- newTVarIO freshGraph
186+ clientNodes <- STMSet.newIO
133187 sessions <- STMMap.newIO
134− return $ Right $ InProcessConnection strat sessions graphTvar
135− MinimalPersistence dbdir -> connectPersistentProjectM36 strat NoDiskSync dbdir freshGraph
136− CrashSafePersistence dbdir -> connectPersistentProjectM36 strat FsyncDiskSync dbdir freshGraph
188+ notificationPid <- startNotificationListener notificationCallback
189+ let conn = InProcessConnection strat clientNodes sessions graphTvar
190+ addClientNode conn notificationPid
191+ pure (Right conn)
192+ MinimalPersistence dbdir -> connectPersistentProjectM36 strat NoDiskSync dbdir freshGraph notificationCallback
193+ CrashSafePersistence dbdir -> connectPersistentProjectM36 strat FsyncDiskSync dbdir freshGraph notificationCallback
137194
138−connectProjectM36 (RemoteProcessConnectionInfo databaseName serverNodeId) = do
195+connectProjectM36 (RemoteProcessConnectionInfo databaseName serverNodeId notificationCallback) = do
139196 connStatus <- newEmptyMVar
140197 eLocalNode <- commonLocalNode
198+ notificationListenerPid <- startNotificationListener notificationCallback
141199 let dbName = remoteDBLookupName databaseName
142200 putStrLn $ show serverNodeId ++ " " ++ dbName
143201 case eLocalNode of
@@ -148,7 +206,7 @@ connectProjectM36 (RemoteProcessConnectionInfo databaseName serverNodeId) = do
148206 case mServerProcessId of
149207 Nothing -> liftIO $ putMVar connStatus $ Left (NoSuchDatabaseByNameError databaseName)
150208 Just serverProcessId -> do
151− loginConfirmation <- call serverProcessId Login
209+ loginConfirmation <- call serverProcessId (Login notificationListenerPid)
152210 if not loginConfirmation then
153211 liftIO $ putMVar connStatus (Left LoginError)
154212 else do
@@ -159,9 +217,10 @@ connectProjectM36 (RemoteProcessConnectionInfo databaseName serverNodeId) = do
159217connectPersistentProjectM36 :: PersistenceStrategy ->
160218 DiskSync ->
161219 FilePath ->
162− TransactionGraph ->
220+ TransactionGraph ->
221+ NotificationCallback ->
163222 IO (Either ConnectionError Connection)
164−connectPersistentProjectM36 strat sync dbdir freshGraph = do
223+connectPersistentProjectM36 strat sync dbdir freshGraph notificationCallback = do
165224 err <- setupDatabaseDir sync dbdir freshGraph
166225 case err of
167226 Just err' -> return $ Left (SetupDatabaseDirectoryError err')
@@ -172,18 +231,22 @@ connectPersistentProjectM36 strat sync dbdir freshGraph = do
172231 Right graph' -> do
173232 tvarGraph <- newTVarIO graph'
174233 sessions <- STMMap.newIO
175− return $ Right $ InProcessConnection strat sessions tvarGraph
176−
234+ clientNodes <- STMSet.newIO
235+ let conn = InProcessConnection strat clientNodes sessions tvarGraph
236+ notificationPid <- startNotificationListener notificationCallback
237+ addClientNode conn notificationPid
238+ pure (Right conn)
239+
177240-- | Create a new session at the transaction UUID and return the session's Id.
178241createSessionAtCommit :: UUID -> Connection -> IO (Either RelationalError SessionId)
179−createSessionAtCommit commitUUID conn@(InProcessConnection _ _ _) = do
242+createSessionAtCommit commitUUID conn@(InProcessConnection _ _ _ _) = do
180243 newSessionId <- nextRandom
181244 atomically $ do
182245 createSessionAtCommit_ commitUUID newSessionId conn
183246createSessionAtCommit uuid conn@(RemoteProcessConnection _ _) = remoteCall conn (CreateSessionAtCommit uuid)
184247
185248createSessionAtCommit_ :: UUID -> SessionId -> Connection -> STM (Either RelationalError SessionId)
186−createSessionAtCommit_ commitUUID newSessionId (InProcessConnection _ sessions graphTvar) = do
249+createSessionAtCommit_ commitUUID newSessionId (InProcessConnection _ _ sessions graphTvar) = do
187250 graph <- readTVar graphTvar
188251 case transactionForUUID commitUUID graph of
189252 Left err -> pure (Left err)
@@ -199,26 +262,31 @@ createSessionAtCommit_ _ _ (RemoteProcessConnection _ _) = error "createSessionA
199262
200263-- | Call 'createSessionAtHead' with a transaction graph's head's name to create a new session pinned to that head. This function returns a 'SessionId' which can be used in other function calls to reference the point in the transaction graph.
201264createSessionAtHead :: HeadName -> Connection -> IO (Either RelationalError SessionId)
202−createSessionAtHead headn conn@(InProcessConnection _ _ tvar) = do
265+createSessionAtHead headn conn@(InProcessConnection _ _ _ graphTvar) = do
203266 newSessionId <- nextRandom
204267 atomically $ do
205− graph <- readTVar tvar
268+ graph <- readTVar graphTvar
206269 case transactionForHead headn graph of
207270 Nothing -> pure $ Left (NoSuchHeadNameError headn)
208271 Just trans -> createSessionAtCommit_ (transactionUUID trans) newSessionId conn
209−createSessionAtHead headn conn@(RemoteProcessConnection _ _) = remoteCall conn (CreateSessionAtHead headn)
272+createSessionAtHead headn conn@(RemoteProcessConnection _ _) = remoteCall conn (CreateSessionAtHead headn)
273+
274+-- | Used internally for server connections to keep track of remote nodes for the purpose of sending notifications later.
275+addClientNode :: Connection -> ProcessId -> IO ()
276+addClientNode (RemoteProcessConnection _ _) _ = error "addClientNode called on remote connection"
277+addClientNode (InProcessConnection _ clientNodes _ _) newProcessId = atomically (STMSet.insert newProcessId clientNodes)
210278
211279-- | Discards a session, eliminating any uncommitted changes present in the session.
212280closeSession :: SessionId -> Connection -> IO ()
213−closeSession sessionId (InProcessConnection _ sessions _) = do
214− atomically $ do
215− STMMap.delete sessionId sessions
281+closeSession sessionId (InProcessConnection _ _ sessions _) = do
282+ atomically $ STMMap.delete sessionId sessions
216283closeSession sessionId conn@(RemoteProcessConnection _ _) = remoteCall conn (CloseSession sessionId)
217284-- | 'close' cleans up the database access connection. Note that sessions persist even after the connection is closed.
218285close :: Connection -> IO ()
219−close (InProcessConnection _ sessions _) = atomically $ do
286+close (InProcessConnection _ _ sessions _) = atomically $ do
220287 traverse_ (\(k,_) -> STMMap.delete k sessions) (STMMap.stream sessions)
221288 pure ()
289+
222290close (RemoteProcessConnection localNode serverProcessId) = do
223291 runProcessResult localNode $ do
224292 call serverProcessId Logout
@@ -232,7 +300,7 @@ runProcessResult localNode proc = do
232300 takeMVar ret
233301
234302remoteCall :: (Serializable a, Serializable b) => Connection -> a -> IO b
235−remoteCall (InProcessConnection _ _ _) _ = error "remoteCall called on local connection"
303+remoteCall (InProcessConnection _ _ _ _) _ = error "remoteCall called on local connection"
236304remoteCall (RemoteProcessConnection localNode serverProcessId) arg = runProcessResult localNode $ do
237305 ret <- safeCall serverProcessId arg
238306 case ret of
@@ -246,7 +314,7 @@ sessionForSessionId sessionId sessions = do
246314
247315-- | Execute a relational expression in the context of the session and connection. Relational expressions are queries and therefore cannot alter the database.
248316executeRelationalExpr :: SessionId -> Connection -> RelationalExpr -> IO (Either RelationalError Relation)
249−executeRelationalExpr sessionId (InProcessConnection _ sessions _) expr = atomically $ do
317+executeRelationalExpr sessionId (InProcessConnection _ _ sessions _) expr = atomically $ do
250318 eSession <- sessionForSessionId sessionId sessions
251319 case eSession of
252320 Left err -> pure $ Left err
@@ -255,7 +323,7 @@ executeRelationalExpr sessionId conn@(RemoteProcessConnection _ _) relExpr = rem
255323
256324-- | 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.
257325executeDatabaseContextExpr :: SessionId -> Connection -> DatabaseExpr -> IO (Maybe RelationalError)
258−executeDatabaseContextExpr sessionId (InProcessConnection _ sessions _) expr = atomically $ do
326+executeDatabaseContextExpr sessionId (InProcessConnection _ _ sessions _) expr = atomically $ do
259327 eSession <- sessionForSessionId sessionId sessions
260328 case eSession of
261329 Left err -> pure $ Just err
@@ -268,14 +336,11 @@ executeDatabaseContextExpr sessionId (InProcessConnection _ sessions _) expr = a
268336 return Nothing
269337executeDatabaseContextExpr sessionId conn@(RemoteProcessConnection _ _) dbExpr = remoteCall conn (ExecuteDatabaseContextExpr sessionId dbExpr)
270338
271−-- | Execute a transaction graph expression in the context of the session and connection. Transaction graph operators modify the transaction graph state.
272−executeGraphExpr :: SessionId -> Connection -> TransactionGraphOperator -> IO (Maybe RelationalError)
273−executeGraphExpr sessionId (InProcessConnection strat sessions graphTvar) graphExpr = do
274− freshUUID <- nextRandom
275− manip <- atomically $ do
276− graph <- readTVar graphTvar
277− eSession <- sessionForSessionId sessionId sessions
278− case eSession of
339+executeGraphExprSTM_ :: UUID -> SessionId -> Sessions -> TransactionGraphOperator -> TVar TransactionGraph -> STM (Either RelationalError TransactionGraph)
340+executeGraphExprSTM_ freshUUID sessionId sessions graphExpr graphTvar = do
341+ graph <- readTVar graphTvar
342+ eSession <- sessionForSessionId sessionId sessions
343+ case eSession of
279344 Left err -> pure $ Left err
280345 Right (Session discon) -> case evalGraphOp freshUUID discon graph graphExpr of
281346 Left err -> pure $ Left err
@@ -284,23 +349,76 @@ executeGraphExpr sessionId (InProcessConnection strat sessions graphTvar) graphE
284349 let newSession = Session discon'
285350 STMMap.insert newSession sessionId sessions
286351 pure $ Right graph'
352+
353+-- process notifications for commits
354+executeCommitExprSTM_ :: DatabaseContext -> DatabaseContext -> ClientNodes -> STM (EvaluatedNotifications, ClientNodes)
355+executeCommitExprSTM_ oldContext newContext nodes = do
356+ let nots = notifications oldContext
357+ fireNots = notificationChanges nots oldContext newContext
358+ evaldNots = M.map mkEvaldNot fireNots
359+ mkEvaldNot notif = EvaluatedNotification { notification = notif, reportRelation = evalState (RE.evalRelationalExpr (reportExpr notif)) oldContext }
360+ pure (evaldNots, nodes)
361+
362+
363+-- | Execute a transaction graph expression in the context of the session and connection. Transaction graph operators modify the transaction graph state.
364+executeGraphExpr :: SessionId -> Connection -> TransactionGraphOperator -> IO (Maybe RelationalError)
365+executeGraphExpr sessionId (InProcessConnection strat clientNodes sessions graphTvar) graphExpr = do
366+ freshUUID <- nextRandom
367+ manip <- atomically $ do
368+ eSession <- sessionForSessionId sessionId sessions
369+ oldGraph <- readTVar graphTvar
370+ case eSession of
371+ Left err -> pure (Left err)
372+ Right (Session (DisconnectedTransaction parentUUID currentContext)) -> do
373+ eGraph <- executeGraphExprSTM_ freshUUID sessionId sessions graphExpr graphTvar
374+ case eGraph of
375+ Left err -> pure (Left err)
376+ Right newGraph -> do
377+ if graphExpr == Commit then
378+ case transactionForUUID parentUUID oldGraph of
379+ Left err -> pure $ Left err
380+ Right (Transaction _ _ previousContext) -> do
381+ (evaldNots, nodes) <- executeCommitExprSTM_ previousContext currentContext clientNodes
382+ nodesToNotify <- toList (STMSet.stream nodes)
383+ pure $ Right (evaldNots, nodesToNotify, newGraph)
384+ else
385+ pure $ Right (M.empty, [], newGraph)
287386 case manip of
288387 Left err -> return $ Just err
289− Right newGraph -> do
388+ Right (notsToFire, nodesToNotify, newGraph) -> do
290389 --update filesystem database, if necessary
291390 --this should really grab a lock at the beginning of the method to be threadsafe
292391 processPersistence strat newGraph
392+ sendNotifications nodesToNotify notsToFire
293393 return Nothing
294394executeGraphExpr sessionId conn@(RemoteProcessConnection _ _) graphExpr = remoteCall conn (ExecuteGraphExpr sessionId graphExpr)
295−
296−-- | After modifying a session, 'commit' the transaction to the transaction graph at the head which the session is referencing.
395+
396+{-
397+commitSTM_ :: UUID -> SessionId -> Sessions -> TVar TransactionGraph -> STM (Maybe RelationalError)
398+commitSTM_ freshUUID sessionId sessions graph = do
399+ mErr <- executeGraphExprSTM_ freshUUID sessionId sessions Commit graph
400+ pure $ case mErr of
401+ Left err -> Just err
402+ Right _ -> Nothing
403+-}
404+
405+-- | After modifying a session, 'commit' the transaction to the transaction graph at the head which the session is referencing. This will also trigger checks for any notifications which need to be propagated.
297406commit :: SessionId -> Connection -> IO (Maybe RelationalError)
298−commit sessionId conn@(InProcessConnection _ _ _) = executeGraphExpr sessionId conn Commit
407+commit sessionId conn@(InProcessConnection _ _ _ _) = executeGraphExpr sessionId conn Commit
299408commit sessionId conn@(RemoteProcessConnection _ _) = remoteCall conn (ExecuteGraphExpr sessionId Commit)
409+
410+sendNotifications :: [ProcessId] -> EvaluatedNotifications -> IO ()
411+sendNotifications pids nots = mapM_ sendNots pids
412+ where
413+ sendNots remoteClientPid = do
414+ eLocalNode <- commonLocalNode
415+ case eLocalNode of
416+ Left err -> error ("Failed to get local node: " ++ show err)
417+ Right localNode -> when (not (M.null nots)) $ runProcess localNode $ send remoteClientPid (NotificationMessage nots)
300418
301419-- | Discard any changes made in the current session. This resets the disconnected transaction to reference the original database context of the parent transaction and is a very cheap operation.
302420rollback :: SessionId -> Connection -> IO (Maybe RelationalError)
303−rollback sessionId conn@(InProcessConnection _ _ _) = executeGraphExpr sessionId conn Rollback
421+rollback sessionId conn@(InProcessConnection _ _ _ _) = executeGraphExpr sessionId conn Rollback
304422rollback sessionId conn@(RemoteProcessConnection _ _) = remoteCall conn (ExecuteGraphExpr sessionId Rollback)
305423
306424processPersistence :: PersistenceStrategy -> TransactionGraph -> IO ()
@@ -310,11 +428,11 @@ processPersistence (CrashSafePersistence dbdir) graph = transactionGraphPersist
310428
311429-- | Return a relation whose type would match that of the relational expression if it were executed. This is useful for checking types and validating a relational expression's types.
312430typeForRelationalExpr :: SessionId -> Connection -> RelationalExpr -> IO (Either RelationalError Relation)
313−typeForRelationalExpr sessionId conn@(InProcessConnection _ _ _) relExpr = atomically $ typeForRelationalExprSTM sessionId conn relExpr
431+typeForRelationalExpr sessionId conn@(InProcessConnection _ _ _ _) relExpr = atomically $ typeForRelationalExprSTM sessionId conn relExpr
314432typeForRelationalExpr sessionId conn@(RemoteProcessConnection _ _) relExpr = remoteCall conn (ExecuteTypeForRelationalExpr sessionId relExpr)
315433
316434typeForRelationalExprSTM :: SessionId -> Connection -> RelationalExpr -> STM (Either RelationalError Relation)
317−typeForRelationalExprSTM sessionId (InProcessConnection _ sessions _) relExpr = do
435+typeForRelationalExprSTM sessionId (InProcessConnection _ _ sessions _) relExpr = do
318436 eSession <- sessionForSessionId sessionId sessions
319437 case eSession of
320438 Left err -> pure $ Left err
@@ -324,7 +442,7 @@ typeForRelationalExprSTM _ _ _ = error "typeForRelationalExprSTM called on non-l
324442
325443-- | Return a 'Map' of the database's constraints at the context of the session and connection.
326444inclusionDependencies :: SessionId -> Connection -> IO (Either RelationalError (M.Map IncDepName InclusionDependency))
327−inclusionDependencies sessionId (InProcessConnection _ sessions _) = do
445+inclusionDependencies sessionId (InProcessConnection _ _ sessions _) = do
328446 atomically $ do
329447 eSession <- sessionForSessionId sessionId sessions
330448 case eSession of
@@ -335,7 +453,7 @@ inclusionDependencies sessionId conn@(RemoteProcessConnection _ _) = remoteCall
335453
336454-- | Return an optimized database expression which is logically equivalent to the input database expression. This function can be used to determine which expression will actually be evaluated.
337455planForDatabaseContextExpr :: SessionId -> Connection -> DatabaseExpr -> IO (Either RelationalError DatabaseExpr)
338−planForDatabaseContextExpr sessionId (InProcessConnection _ sessions _) dbExpr = do
456+planForDatabaseContextExpr sessionId (InProcessConnection _ _ sessions _) dbExpr = do
339457 atomically $ do
340458 eSession <- sessionForSessionId sessionId sessions
341459 case eSession of
@@ -349,7 +467,7 @@ planForDatabaseContextExpr sessionId conn@(RemoteProcessConnection _ _) dbExpr =
349467-- * id- UUID attribute of the transaction
350468-- * parents- a relation-valued attribute which contains a relation of UUIDs which are parent transaction to the transaction
351469transactionGraphAsRelation :: SessionId -> Connection -> IO (Either RelationalError Relation)
352−transactionGraphAsRelation sessionId (InProcessConnection _ sessions tvar) = do
470+transactionGraphAsRelation sessionId (InProcessConnection _ _ sessions tvar) = do
353471 atomically $ do
354472 eSession <- sessionForSessionId sessionId sessions
355473 case eSession of
@@ -362,7 +480,7 @@ transactionGraphAsRelation sessionId conn@(RemoteProcessConnection _ _) = remote
362480
363481-- | Returns the UUID for the connection's disconnected transaction committed parent transaction.
364482headTransactionUUID :: SessionId -> Connection -> IO (Maybe UUID)
365−headTransactionUUID sessionId (InProcessConnection _ sessions _) = do
483+headTransactionUUID sessionId (InProcessConnection _ _ sessions _) = do
366484 atomically $ do
367485 eSession <- sessionForSessionId sessionId sessions
368486 case eSession of
@@ -370,10 +488,8 @@ headTransactionUUID sessionId (InProcessConnection _ sessions _) = do
370488 Right session -> pure $ Just (sessionParentUUID session)
371489headTransactionUUID sessionId conn@(RemoteProcessConnection _ _) = remoteCall conn (RetrieveHeadTransactionUUID sessionId)
372490
373−-- | Returns Just the name of the head of the current disconnected transaction or Nothing.
374−headName :: SessionId -> Connection -> IO (Maybe HeadName)
375−headName sessionId (InProcessConnection _ sessions graphTvar) = do
376− atomically $ do
491+headNameSTM_ :: SessionId -> Sessions -> TVar TransactionGraph -> STM (Maybe HeadName)
492+headNameSTM_ sessionId sessions graphTvar = do
377493 graph <- readTVar graphTvar
378494 eSession <- sessionForSessionId sessionId sessions
379495 case eSession of
@@ -381,6 +497,11 @@ headName sessionId (InProcessConnection _ sessions graphTvar) = do
381497 Right session -> pure $ case transactionForUUID (sessionParentUUID session) graph of
382498 Left _ -> Nothing
383499 Right parentTrans -> headNameForTransaction parentTrans graph
500+
501+-- | Returns Just the name of the head of the current disconnected transaction or Nothing.
502+headName :: SessionId -> Connection -> IO (Maybe HeadName)
503+headName sessionId (InProcessConnection _ _ sessions graphTvar) = do
504+ atomically (headNameSTM_ sessionId sessions graphTvar)
384505headName sessionId conn@(RemoteProcessConnection _ _) = remoteCall conn (ExecuteHeadName sessionId)
385506
386507