Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
haskell-gargantext
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
160
Issues
160
List
Board
Labels
Milestones
Merge Requests
14
Merge Requests
14
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
gargantext
haskell-gargantext
Commits
e0326e9c
Verified
Commit
e0326e9c
authored
Jul 29, 2024
by
Przemyslaw Kaminski
1
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[websockets] send job progress in notification
Also, throttle messages to avoid flooding the client
parent
39f8f17d
Pipeline
#6471
failed with stages
in 10 minutes and 23 seconds
Changes
8
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
75 additions
and
53 deletions
+75
-53
cabal.project
cabal.project
+7
-1
gargantext.cabal
gargantext.cabal
+1
-0
EnvTypes.hs
src/Gargantext/API/Admin/EnvTypes.hs
+11
-5
CentralExchange.hs
src/Gargantext/Core/AsyncUpdates/CentralExchange.hs
+2
-2
Types.hs
src/Gargantext/Core/AsyncUpdates/CentralExchange/Types.hs
+7
-11
Dispatcher.hs
src/Gargantext/Core/AsyncUpdates/Dispatcher.hs
+39
-26
Types.hs
src/Gargantext/Core/AsyncUpdates/Dispatcher/Types.hs
+7
-8
Internal.hs
src/Gargantext/Utils/Jobs/Internal.hs
+1
-0
No files found.
cabal.project
View file @
e0326e9c
...
...
@@ -108,7 +108,7 @@ source-repository-package
source
-
repository
-
package
type
:
git
location
:
https
://
gitlab
.
iscpif
.
fr
/
gargantext
/
crawlers
/
openalex
.
git
tag
:
c2114adb0382770e419e5a7ae1b3a1ee5b09ee50
tag
:
e805de664576030e687f4e72e14d2eb3a20dc8a1
source
-
repository
-
package
type
:
git
...
...
@@ -185,6 +185,12 @@ source-repository-package
location
:
https
://
github
.
com
/
fpringle
/
servant
-
routes
.
git
tag
:
7694f62
af6bc1596d754b42af16da131ac403b3a
source
-
repository
-
package
type
:
git
location
:
https
://
gitlab
.
iscpif
.
fr
/
gargantext
/
haskell
-
throttle
tag
:
02f5
ed9ee2d6cce45161addf945b88bc6adf9059
allow
-
older
:
*
allow
-
newer
:
*
...
...
gargantext.cabal
View file @
e0326e9c
...
...
@@ -571,6 +571,7 @@ library
, graphviz ^>= 2999.20.1.0
, hashable ^>= 1.3.0.0
, haskell-igraph ^>= 0.10.4
, haskell-throttle
, hlcm ^>= 0.2.2
, hsinfomap ^>= 0.1
, hsparql ^>= 0.3.8
...
...
src/Gargantext/API/Admin/EnvTypes.hs
View file @
e0326e9c
...
...
@@ -49,6 +49,7 @@ import Gargantext.Prelude
import
Gargantext.Core.Config
(
GargConfig
(
..
))
import
Gargantext.Core.Config.Mail
(
MailConfig
)
import
Gargantext.System.Logging
import
Gargantext.Utils.Jobs.Internal
(
pollJob
)
import
Gargantext.Utils.Jobs.Map
(
LoggerM
,
J
(
..
),
jTask
,
rjGetLog
)
import
Gargantext.Utils.Jobs.Monad
qualified
as
Jobs
import
Network.HTTP.Client
(
Manager
)
...
...
@@ -56,6 +57,7 @@ import Servant.Client (BaseUrl)
import
Servant.Job.Async
(
HasJobEnv
(
..
),
Job
)
import
Servant.Job.Async
qualified
as
SJ
import
Servant.Job.Core
qualified
import
Servant.Job.Types
qualified
as
SJ
import
System.Log.FastLogger
qualified
as
FL
data
Mode
=
Dev
|
Mock
|
Prod
...
...
@@ -197,11 +199,15 @@ updateJobProgress hdl@(JobHandle jId logStatus) updateJobStatus = do
jobLog
<-
Jobs
.
getLatestJobStatus
hdl
let
jobLogNew
=
updateJobStatus
jobLog
logStatus
jobLogNew
CET
.
ce_notify
$
CET
.
UpdateJobProgress
jId
jobLogNew
-- mJob <- Jobs.findJob jId
-- case mJob of
-- Nothing -> pure ()
-- Just job -> liftBase $ CE.ce_notify $ CET.UpdateJobProgress jId job
mJb
<-
Jobs
.
findJob
jId
case
mJb
of
Nothing
->
pure
()
Just
je
->
do
-- We use the same endpoint as the one for polling jobs via
-- API. This way we can send the job status directly in the
-- notification
j
<-
pollJob
(
Just
$
SJ
.
Limit
1
)
Nothing
jId
je
CET
.
ce_notify
$
CET
.
UpdateJobProgress
j
instance
Jobs
.
MonadJobStatus
(
GargM
Env
err
)
where
...
...
src/Gargantext/Core/AsyncUpdates/CentralExchange.hs
View file @
e0326e9c
...
...
@@ -64,8 +64,8 @@ gServer = do
forever
$
do
r
<-
atomically
$
TChan
.
readTChan
tChan
case
Aeson
.
decode
(
BSL
.
fromStrict
r
)
of
Just
ujp
@
(
UpdateJobProgress
_jId
_jobLog
)
->
do
logMsg
ioLogger
DEBUG
$
"[central_exchange] "
<>
show
ujp
Just
_ujp
@
(
UpdateJobProgress
_s
)
->
do
--
logMsg ioLogger DEBUG $ "[central_exchange] " <> show ujp
-- send the same message that we received
send
s_dispatcher
r
Just
(
UpdateTreeFirstLevel
node_id
)
->
do
...
...
src/Gargantext/Core/AsyncUpdates/CentralExchange/Types.hs
View file @
e0326e9c
...
...
@@ -25,7 +25,7 @@ import Gargantext.Core.Types (NodeId)
import
Gargantext.Prelude
import
Prelude
qualified
import
Servant.Job.Core
(
Safety
(
Safe
))
import
Servant.Job.Types
(
Job
ID
)
import
Servant.Job.Types
(
Job
Status
)
{-
...
...
@@ -37,31 +37,27 @@ various events).
-- INTERNAL MESSAGES
data
CEMessage
=
-- UpdateJobProgress (JobID 'Safe) (JM.JobEntry (JobID 'Safe) (Seq JobLog) JobLog)
UpdateJobProgress
(
JobID
'S
a
fe
)
JobLog
UpdateJobProgress
(
JobStatus
'S
a
fe
JobLog
)
|
UpdateTreeFirstLevel
NodeId
deriving
(
Eq
)
instance
Prelude
.
Show
CEMessage
where
show
(
UpdateJobProgress
j
Id
jobLog
)
=
"UpdateJobProgress "
<>
(
CBUTF8
.
decode
$
BSL
.
unpack
$
Aeson
.
encode
jId
)
<>
" "
<>
show
jobLog
show
(
UpdateJobProgress
j
s
)
=
"UpdateJobProgress "
<>
(
CBUTF8
.
decode
$
BSL
.
unpack
$
Aeson
.
encode
js
)
show
(
UpdateTreeFirstLevel
nodeId
)
=
"UpdateTreeFirstLevel "
<>
show
nodeId
instance
FromJSON
CEMessage
where
parseJSON
=
withObject
"CEMessage"
$
\
o
->
do
type_
<-
o
.:
"type"
case
type_
of
"update_job_progress"
->
do
jId
<-
o
.:
"j_id"
jobLog
<-
o
.:
"job_log"
pure
$
UpdateJobProgress
jId
jobLog
js
<-
o
.:
"js"
pure
$
UpdateJobProgress
js
"update_tree_first_level"
->
do
node_id
<-
o
.:
"node_id"
pure
$
UpdateTreeFirstLevel
node_id
s
->
prependFailure
"parsing type failed, "
(
typeMismatch
"type"
s
)
instance
ToJSON
CEMessage
where
toJSON
(
UpdateJobProgress
j
Id
jobLog
)
=
object
[
toJSON
(
UpdateJobProgress
j
s
)
=
object
[
"type"
.=
toJSON
(
"update_job_progress"
::
Text
)
,
"j_id"
.=
toJSON
jId
,
"job_log"
.=
toJSON
jobLog
,
"js"
.=
toJSON
js
]
toJSON
(
UpdateTreeFirstLevel
node_id
)
=
object
[
"type"
.=
toJSON
(
"update_tree_first_level"
::
Text
)
...
...
src/Gargantext/Core/AsyncUpdates/Dispatcher.hs
View file @
e0326e9c
...
...
@@ -20,6 +20,7 @@ module Gargantext.Core.AsyncUpdates.Dispatcher where
import
Control.Concurrent.Async
qualified
as
Async
import
Control.Concurrent.STM.TChan
qualified
as
TChan
import
Control.Concurrent.Throttle
(
throttle
)
import
Data.Aeson
qualified
as
Aeson
import
Data.ByteString.Lazy
qualified
as
BSL
import
DeferredFolds.UnfoldlM
qualified
as
UnfoldlM
...
...
@@ -30,7 +31,8 @@ import Gargantext.Prelude
import
Gargantext.System.Logging
(
LogLevel
(
DEBUG
),
withLogger
,
logMsg
)
import
Nanomsg
(
Pull
(
..
),
bind
,
recvMalloc
,
withSocket
)
import
Network.WebSockets
qualified
as
WS
import
StmContainers.Set
as
SSet
import
Servant.Job.Types
(
JobStatus
(
_job_id
))
import
StmContainers.Set
qualified
as
SSet
{-
...
...
@@ -66,17 +68,20 @@ dispatcherListener subscriptions = do
tChan
<-
TChan
.
newTChanIO
throttleTChan
<-
TChan
.
newTChanIO
-- NOTE I'm not sure that we need more than 1 worker here, but in
-- theory, the worker can perform things like user authentication,
-- DB queries etc so it can be slow sometimes.
void
$
Async
.
concurrently
(
Async
.
replicateConcurrently
5
$
worker
tChan
)
$
do
forever
$
do
-- putText "[dispatcher_listener] receiving"
r
<-
recvMalloc
s
1024
-- C.putStrLn $ "[dispatcher_listener] " <> r
atomically
$
TChan
.
writeTChan
tChan
r
Async
.
withAsync
(
throttle
500
throttleTChan
sendDataMessageThrottled
)
$
\
_
->
do
void
$
Async
.
concurrently
(
Async
.
replicateConcurrently
5
$
worker
tChan
throttleTChan
)
$
do
forever
$
do
-- putText "[dispatcher_listener] receiving"
r
<-
recvMalloc
s
1024
-- C.putStrLn $ "[dispatcher_listener] " <> r
atomically
$
TChan
.
writeTChan
tChan
r
where
worker
tChan
=
do
worker
tChan
throttleTChan
=
do
-- tId <- myThreadId
forever
$
do
...
...
@@ -100,22 +105,30 @@ dispatcherListener subscriptions = do
-- one drops in the meantime, it won't listen to what we
-- send...)
-- let filteredSubs = filterCEMessageSubs ceMessage subs
mapM_
(
sendNotification
ceMessage
)
filteredSubs
sendNotification
::
CETypes
.
CEMessage
->
Subscription
->
IO
()
sendNotification
ceMessage
sub
=
do
let
ws
=
s_ws_key_connection
sub
let
topic
=
s_topic
sub
notification
<-
case
ceMessage
of
CETypes
.
UpdateJobProgress
_jId
jobLog
->
do
-- js <- getLatestJobStatus jId
-- putText $ "[sendNotification] latestJobStatus" js
pure
$
Notification
topic
(
MJobProgress
jobLog
)
CETypes
.
UpdateTreeFirstLevel
_nodeId
->
pure
$
Notification
topic
MEmpty
-- TODO send the same thing to everyone for now, this should be
-- converted to notifications
WS
.
sendDataMessage
(
wsConn
ws
)
(
WS
.
Text
(
Aeson
.
encode
notification
)
Nothing
)
mapM_
(
sendNotification
throttleTChan
ceMessage
)
filteredSubs
-- | When processing tasks such as Flow, we can generate quite a few
-- notifications in a short time. We want to limit this with throttle
-- tchan.
sendNotification
::
TChan
.
TChan
((
ByteString
,
Topic
),
(
WS
.
Connection
,
WS
.
DataMessage
))
->
CETypes
.
CEMessage
->
Subscription
->
IO
()
sendNotification
throttleTChan
ceMessage
sub
=
do
let
ws
=
s_ws_key_connection
sub
let
topic
=
s_topic
sub
notification
<-
case
ceMessage
of
CETypes
.
UpdateJobProgress
jobStatus
->
do
pure
$
Notification
topic
(
MJobProgress
jobStatus
)
CETypes
.
UpdateTreeFirstLevel
_nodeId
->
pure
$
Notification
topic
MEmpty
let
id'
=
(
wsKey
ws
,
topic
)
atomically
$
TChan
.
writeTChan
throttleTChan
(
id'
,
(
wsConn
ws
,
WS
.
Text
(
Aeson
.
encode
notification
)
Nothing
))
sendDataMessageThrottled
::
(
WS
.
Connection
,
WS
.
DataMessage
)
->
IO
()
sendDataMessageThrottled
(
conn
,
msg
)
=
WS
.
sendDataMessage
conn
msg
-- Custom filtering of list of Subscriptions based on
...
...
@@ -126,7 +139,7 @@ filterCEMessageSubs :: CETypes.CEMessage -> [Subscription] -> [Subscription]
filterCEMessageSubs
ceMessage
subscriptions
=
filter
(
ceMessageSubPred
ceMessage
)
subscriptions
ceMessageSubPred
::
CETypes
.
CEMessage
->
Subscription
->
Bool
ceMessageSubPred
(
CETypes
.
UpdateJobProgress
j
Id
_jobLog
)
(
Subscription
{
s_topic
})
=
s_topic
==
UpdateJobProgress
jId
ceMessageSubPred
(
CETypes
.
UpdateJobProgress
j
s
)
(
Subscription
{
s_topic
})
=
s_topic
==
(
UpdateJobProgress
$
_job_id
js
)
ceMessageSubPred
(
CETypes
.
UpdateTreeFirstLevel
node_id
)
(
Subscription
{
s_topic
})
=
s_topic
==
UpdateTree
node_id
src/Gargantext/Core/AsyncUpdates/Dispatcher/Types.hs
View file @
e0326e9c
...
...
@@ -47,7 +47,7 @@ import Servant
import
Servant.API.WebSocket
qualified
as
WS
import
Servant.Auth.Server
(
verifyJWT
)
import
Servant.Job.Core
(
Safety
(
Safe
))
import
Servant.Job.Types
(
JobID
)
import
Servant.Job.Types
(
JobID
,
JobStatus
)
import
Servant.Server.Generic
(
AsServer
,
AsServerT
)
import
StmContainers.Set
as
SSet
...
...
@@ -62,7 +62,7 @@ data Topic =
-- | Given parent node id, trigger update of the node and its
-- children (e.g. list is automatically created in a corpus)
|
UpdateTree
NodeId
deriving
(
Eq
)
deriving
(
Eq
,
Ord
)
instance
Prelude
.
Show
Topic
where
show
(
UpdateJobProgress
jId
)
=
"UpdateJobProgress "
<>
(
CBUTF8
.
decode
$
BSL
.
unpack
$
Aeson
.
encode
jId
)
show
(
UpdateTree
nodeId
)
=
"UpdateTree "
<>
show
nodeId
...
...
@@ -92,16 +92,15 @@ instance ToJSON Topic where
-- | A message to be sent inside a Notification
data
Message
=
MJobProgress
JobLog
MJobProgress
(
JobStatus
'S
a
fe
JobLog
)
|
MEmpty
deriving
(
Eq
)
instance
Prelude
.
Show
Message
where
show
(
MJobProgress
job
Progress
)
=
"MJobProgress "
<>
show
jobProgress
show
(
MJobProgress
job
Status
)
=
"MJobProgress "
<>
(
CBUTF8
.
decode
$
BSL
.
unpack
$
Aeson
.
encode
jobStatus
)
show
MEmpty
=
"MEmpty"
instance
ToJSON
Message
where
toJSON
(
MJobProgress
job
Progres
s
)
=
Aeson
.
object
[
toJSON
(
MJobProgress
job
Statu
s
)
=
Aeson
.
object
[
"type"
.=
toJSON
(
"MJobProgress"
::
Text
)
,
"job_
progress"
.=
toJSON
jobProgres
s
,
"job_
status"
.=
toJSON
jobStatu
s
]
toJSON
MEmpty
=
Aeson
.
object
[
"type"
.=
toJSON
(
"MEmpty"
::
Text
)
...
...
@@ -202,7 +201,7 @@ class HasDispatcher env where
-- | A notification is sent to clients who subscribed to specific topics
data
Notification
=
Notification
Topic
Message
deriving
(
Eq
,
Show
)
deriving
(
Show
)
instance
ToJSON
Notification
where
toJSON
(
Notification
topic
message
)
=
Aeson
.
object
[
...
...
src/Gargantext/Utils/Jobs/Internal.hs
View file @
e0326e9c
...
...
@@ -5,6 +5,7 @@ module Gargantext.Utils.Jobs.Internal (
serveJobsAPI
-- * Internals for testing
,
newJob
,
pollJob
)
where
import
Control.Concurrent
...
...
Przemyslaw Kaminski
@cgenie
mentioned in commit
5660aec0
·
Oct 08, 2024
mentioned in commit
5660aec0
mentioned in commit 5660aec07ec5a0a0a5468f440092c1a8f57a864e
Toggle commit list
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment