Skip to content

Commit 61c027b

Browse files
authored
feat: extension for remote R session management (#24)
* feat: added session related S4 methods * feat: datashield.sessions added * feat: use workspace restore and assignment functions in login function to handle R sessions properly * feat: display state of each R server * feat: ensure backward compatibility with older DSI implementations (ie not having session support) * fix: typo * fix: ensure R sessions before perfoming resource assignment * chore: update roxygen * fix: update progress when not async session creation
1 parent 8d23e83 commit 61c027b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+665
-168
lines changed

DESCRIPTION

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ License: LGPL (>= 2.1)
3636
URL: https://github.com/datashield/DSI/,
3737
https://datashield.github.io/DSI/, https://datashield.org/
3838
BugReports: https://github.com/datashield/DSI/issues
39-
RoxygenNote: 7.3.2
39+
RoxygenNote: 7.3.3
4040
Encoding: UTF-8
4141
Collate:
4242
'DSObject.R'
@@ -45,6 +45,7 @@ Collate:
4545
'DSI-package.R'
4646
'DSLoginBuilder.R'
4747
'DSResult.R'
48+
'DSSession.R'
4849
'datashield.aggregate.R'
4950
'datashield.assign.R'
5051
'datashield.connections.R'
@@ -53,6 +54,7 @@ Collate:
5354
'datashield.list.R'
5455
'datashield.login.R'
5556
'datashield.logout.R'
57+
'datashield.sessions.R'
5658
'datashield.status.R'
5759
'datashield.symbol.R'
5860
'datashield.workspace.R'

NAMESPACE

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export(datashield.profiles)
2121
export(datashield.resource_status)
2222
export(datashield.resources)
2323
export(datashield.rm)
24+
export(datashield.sessions)
2425
export(datashield.symbols)
2526
export(datashield.table_status)
2627
export(datashield.tables)
@@ -37,9 +38,11 @@ export(dsDisconnect)
3738
export(dsFetch)
3839
export(dsGetInfo)
3940
export(dsHasResource)
41+
export(dsHasSession)
4042
export(dsHasTable)
4143
export(dsIsAsync)
4244
export(dsIsCompleted)
45+
export(dsIsReady)
4346
export(dsKeepAlive)
4447
export(dsListMethods)
4548
export(dsListPackages)
@@ -52,11 +55,14 @@ export(dsRestoreWorkspace)
5255
export(dsRmSymbol)
5356
export(dsRmWorkspace)
5457
export(dsSaveWorkspace)
58+
export(dsSession)
59+
export(dsStateMessage)
5560
export(newDSLoginBuilder)
5661
exportClasses(DSConnection)
5762
exportClasses(DSDriver)
5863
exportClasses(DSObject)
5964
exportClasses(DSResult)
65+
exportClasses(DSSession)
6066
import(R6)
6167
import(progress)
6268
importFrom(cli,cli_abort)

R/DSConnection.R

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ setClass("DSConnection", representation(name = "character"), contains = c("DSObj
2929
#' accessible through this connection.
3030
#'
3131
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
32-
#'
3332
#' @return A character vector of table names.
3433
#'
3534
#' @family DSConnection generics
@@ -53,6 +52,7 @@ setGeneric("dsListTables",
5352
#'
5453
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
5554
#' @param table the table fully qualified name
55+
#' @return A logical indicating if the table exists.
5656
#'
5757
#' @family DSConnection generics
5858
#' @examples
@@ -73,6 +73,7 @@ setGeneric("dsHasTable",
7373
#' accessible through this connection.
7474
#'
7575
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
76+
#' @return A character vector of resource names.
7677
#'
7778
#' @family DSConnection generics
7879
#' @examples
@@ -94,6 +95,7 @@ setGeneric("dsListResources",
9495
#'
9596
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
9697
#' @param resource the resource fully qualified name
98+
#' @return A logical indicating if the resource exists.
9799
#'
98100
#' @family DSConnection generics
99101
#' @examples
@@ -108,6 +110,52 @@ setGeneric("dsHasResource",
108110
def = function(conn, resource) standardGeneric("dsHasResource"),
109111
valueClass = "logical")
110112

113+
#' Check remote R session exists
114+
#'
115+
#' Check if a remote R session exists (not necessarily running and ready to accept
116+
#' R commands submissions).
117+
#'
118+
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
119+
#' @return A logical indicating if a remote R session exists accessible through this connection.
120+
#'
121+
#' @family DSConnection generics
122+
#' @examples
123+
#' \dontrun{
124+
#' con <- dsConnect(DSOpal::Opal(), "server1",
125+
#' username = "dsuser", password = "password", url = "https://opal-demo.obiba.org")
126+
#' dsHasSession(con)
127+
#' dsDisconnect(con)
128+
#' }
129+
#' @export
130+
setGeneric("dsHasSession",
131+
def = function(conn) standardGeneric("dsHasSession"),
132+
valueClass = "logical")
133+
134+
#' Create a remote R session
135+
#'
136+
#' Create a remote R session if none exists. If a remote R session already exists,
137+
#' it will be reused. Returns a logical indicating if a remote R session exists
138+
#' accessible through this connection.
139+
#'
140+
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
141+
#' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default)
142+
#' the calls are parallelized over the connections, when the connection supports
143+
#' that feature, with an extra overhead of requests.
144+
#' @return An object of class \code{\link{DSSession-class}} representing the remote R session.
145+
#'
146+
#' @family DSConnection generics
147+
#' @examples
148+
#' \dontrun{
149+
#' con <- dsConnect(DSOpal::Opal(), "server1",
150+
#' username = "dsuser", password = "password", url = "https://opal-demo.obiba.org")
151+
#' dsSession(con, async=TRUE)
152+
#' dsDisconnect(con)
153+
#' }
154+
#' @export
155+
setGeneric("dsSession",
156+
def = function(conn, async=TRUE) standardGeneric("dsSession"),
157+
valueClass = "DSSession")
158+
111159
#' Assign a data table
112160
#'
113161
#' Assign a data table from the data repository to a symbol in the DataSHIELD R session.
@@ -126,7 +174,8 @@ setGeneric("dsHasResource",
126174
#' will be the data frame row names. When specified this column can be used to perform joins between data frames.
127175
#' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over
128176
#' the connections, when the connection supports that feature, with an extra overhead of requests.
129-
#'
177+
#' @return An object of class \code{\link{DSResult-class}} representing the result of the assignment operation.
178+
#'
130179
#' @family DSConnection generics
131180
#' @examples
132181
#' \dontrun{
@@ -150,7 +199,8 @@ setGeneric("dsAssignTable",
150199
#' @param resource Fully qualified name of a resource reference in the data repository.
151200
#' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over
152201
#' the connections, when the connection supports that feature, with an extra overhead of requests.
153-
#'
202+
#' @return An object of class \code{\link{DSResult-class}} representing the result of the assignment operation.
203+
#'
154204
#' @family DSConnection generics
155205
#' @examples
156206
#' \dontrun{
@@ -174,7 +224,8 @@ setGeneric("dsAssignResource",
174224
#' @param expr A R expression with allowed assign functions calls.
175225
#' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over
176226
#' the connections, when the connection supports that feature, with an extra overhead of requests.
177-
#'
227+
#' @return An object of class \code{\link{DSResult-class}} representing the result of the assignment operation.
228+
#'
178229
#' @family DSConnection generics
179230
#' @examples
180231
#' \dontrun{
@@ -197,7 +248,8 @@ setGeneric("dsAssignExpr",
197248
#' @param expr Expression to evaluate.
198249
#' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over
199250
#' the connections, when the connection supports that feature, with an extra overhead of requests.
200-
#'
251+
#' @return An object of class \code{\link{DSResult-class}} representing the result of the aggregation operation.
252+
#'
201253
#' @family DSConnection generics
202254
#' @examples
203255
#' \dontrun{
@@ -217,6 +269,7 @@ setGeneric("dsAggregate",
217269
#' After assignments have been performed, some symbols live in the DataSHIELD R session on the server side.
218270
#'
219271
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
272+
#' @return A character vector of symbol names.
220273
#'
221274
#' @family DSConnection generics
222275
#' @examples
@@ -257,8 +310,8 @@ setGeneric("dsRmSymbol",
257310
#' Get the list of DataSHIELD profiles that have been configured on the remote data repository.
258311
#'
259312
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
260-
#'
261-
#' @return A list containing the "available" character vector of profile names and the "current" profile (in case a default one was assigned).
313+
#' @return A list containing the "available" character vector of profile names and
314+
#' the "current" profile (in case a default one was assigned).
262315
#'
263316
#' @family DSConnection generics
264317
#' @examples
@@ -279,7 +332,6 @@ setGeneric("dsListProfiles",
279332
#'
280333
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
281334
#' @param type Type of the method: "aggregate" (default) or "assign".
282-
#'
283335
#' @return A data.frame with columns: name, type ('aggregate' or 'assign'), class ('function' or 'script'), value, package, version.
284336
#'
285337
#' @family DSConnection generics
@@ -300,7 +352,6 @@ setGeneric("dsListMethods",
300352
#' Get the list of DataSHIELD packages with their version, that have been configured on the remote data repository.
301353
#'
302354
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
303-
#'
304355
#' @return A data.frame with columns: name, version.
305356
#'
306357
#' @family DSConnection generics
@@ -322,7 +373,6 @@ setGeneric("dsListPackages",
322373
#' Get the list of DataSHIELD workspaces, that have been saved on the remote data repository.
323374
#'
324375
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
325-
#'
326376
#' @return A data.frame with columns: name, lastAccessDate, size.
327377
#'
328378
#' @family DSConnection generics
@@ -407,10 +457,11 @@ setGeneric("dsRmWorkspace",
407457
#' When a \code{\link{DSResult-class}} object is returned on aggregation or assignment operation,
408458
#' the raw result can be accessed asynchronously, allowing parallelization of DataSHIELD calls
409459
#' over multpile servers. The returned named list of logicals will specify if asynchronicity is supported for:
410-
#' aggregation operation ('aggregate'), table assignment operation ('assignTable'),
460+
#' session operation ('session'), aggregation operation ('aggregate'), table assignment operation ('assignTable'),
411461
#' resource assignment operation ('assignResource') and expression assignment operation ('assignExpr').
412462
#'
413463
#' @param conn An object that inherits from \code{\link{DSConnection-class}}.
464+
#' @return A named list of logicals indicating if asynchronicity is supported for session, aggregation and assignment operations.
414465
#'
415466
#' @family DSConnection generics
416467
#' @examples

R/DSSession.R

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#' DSSession class
2+
#'
3+
#' This virtual class describes the state of the R session.
4+
#'
5+
#' The default show method displays a summary of the R session state using other
6+
#' DS generics.
7+
#'
8+
#' @name DSSession-class
9+
#' @docType class
10+
#' @family DS classes
11+
#' @family DSSession generics
12+
#' @export
13+
#' @include DSObject.R
14+
setClass("DSSession", contains = c("DSObject", "VIRTUAL"))
15+
16+
#' Get whether the remote R session is up and running
17+
#'
18+
#' Get whether the remote R session is up and running, ready to accept R commands.
19+
#' The primary use of this function is to know whether the session is ready after it has been
20+
#' created in an asynchronous way.
21+
#'
22+
#' @param session An object inheriting from \code{\link{DSSession-class}}.
23+
#' @return A logical
24+
#'
25+
#' @family DSSession generics
26+
#' @examples
27+
#' \dontrun{
28+
#' con <- dsConnect(DSOpal::Opal(), "server1",
29+
#' username = "dsuser", password = "password", url = "https://opal-demo.obiba.org")
30+
#' session <- dsSession(con, async = TRUE)
31+
#' ready <- dsIsReady(session)
32+
#' while (!ready) {
33+
#' Sys.sleep(1)
34+
#' ready <- dsIsReady(session)
35+
#' cat(".")
36+
#' }
37+
#' dsDisconnect(con)
38+
#' }
39+
#' @export
40+
setGeneric("dsIsReady",
41+
def = function(session) standardGeneric("dsIsReady"))
42+
43+
#' Get the state of the remote R session
44+
#'
45+
#' Get a human-readable message that informs about the state of the remote R session.
46+
#' The primary use of this function is to inform the user about the session state process
47+
#' after it has been created in an asynchronous way.
48+
#'
49+
#' @param session An object inheriting from \code{\link{DSSession-class}}.
50+
#' @return A character string
51+
#'
52+
#' @family DSSession generics
53+
#' @examples
54+
#' \dontrun{
55+
#' con <- dsConnect(DSOpal::Opal(), "server1",
56+
#' username = "dsuser", password = "password", url = "https://opal-demo.obiba.org")
57+
#' session <- dsSession(con, async = TRUE)
58+
#' ready <- dsIsReady(session)
59+
#' while (!ready) {
60+
#' Sys.sleep(1)
61+
#' ready <- dsIsReady(session)
62+
#' cat(dsStateMessage(session), "\n")
63+
#' }
64+
#' dsDisconnect(con)
65+
#' }
66+
#' @export
67+
setGeneric("dsStateMessage",
68+
def = function(session) standardGeneric("dsStateMessage"))

R/datashield.aggregate.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#'
4141
#' @export
4242
datashield.aggregate <- function(conns, expr, async=TRUE, success=NULL, error=NULL, errors.print = getOption("datashield.errors.print", FALSE)) {
43+
datashield.sessions(conns)
4344
.clearLastErrors()
4445
rval <- NULL
4546

R/datashield.assign.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ datashield.assign <- function(conns, symbol, value, variables=NULL, missings=FAL
114114
#' }
115115
#' @export
116116
datashield.assign.table <- function(conns, symbol, table, variables=NULL, missings=FALSE, identifiers=NULL, id.name=NULL, async=TRUE, success=NULL, error=NULL, errors.print = getOption("datashield.errors.print", FALSE)) {
117+
datashield.sessions(conns)
117118
.clearLastErrors()
118119
if (is.null(table) || length(table) == 0) {
119120
stop("Not a valid table name", call.=FALSE)
@@ -269,6 +270,7 @@ datashield.assign.table <- function(conns, symbol, table, variables=NULL, missin
269270
#' }
270271
#' @export
271272
datashield.assign.resource <- function(conns, symbol, resource, async=TRUE, success=NULL, error=NULL, errors.print = getOption("datashield.errors.print", FALSE)) {
273+
datashield.sessions(conns)
272274
.clearLastErrors()
273275
if (is.null(resource) || length(resource) == 0) {
274276
stop("Not a valid resource name", call.=FALSE)
@@ -415,6 +417,7 @@ datashield.assign.resource <- function(conns, symbol, resource, async=TRUE, succ
415417
#' }
416418
#' @export
417419
datashield.assign.expr <- function(conns, symbol, expr, async=TRUE, success=NULL, error=NULL, errors.print = getOption("datashield.errors.print", FALSE)) {
420+
datashield.sessions(conns)
418421
.clearLastErrors()
419422

420423
# prepare expressions as a named list

0 commit comments

Comments
 (0)