From 32ff5acf176681a140557ae77b25a8270f9b0097 Mon Sep 17 00:00:00 2001 From: wlandau Date: Thu, 18 Apr 2024 17:20:55 -0400 Subject: [PATCH] descale() --- NEWS.md | 2 +- R/crew_controller.R | 11 ++++++++ R/crew_controller_group.R | 9 +++++++ man/crew_class_controller.Rd | 28 +++++++++++++++++++++ man/crew_class_controller_group.Rd | 23 +++++++++++++++++ tests/interactive/test-promises-groups.R | 5 +++- tests/interactive/test-promises.R | 6 ++++- tests/testthat/test-crew_controller.R | 7 ++++++ tests/testthat/test-crew_controller_group.R | 8 ++++++ 9 files changed, 96 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 20ab5d08..70f7722f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,7 @@ # crew 0.9.2 * Use `.args` rather than `...` in `mirai::mirai()` to make sure arguments continue to be passed as local variables in `mirai` >= 0.13.1.9012. -* Add new controller methods `autoscale()` and `started()` to facilitate different kinds of Shiny apps. +* Add new controller methods `autoscale()`, `descale()`, and `started()` to facilitate different kinds of Shiny apps. * Deprecate the `scale` and `throttle` methods of `controller$promise()`. `promise()` now always calls `autoscale()` to make sure one and only one auto-scaling loop is running asynchronously. Auto-scaling thus continues even after the promise resolves. * Add a second example vignette that simulates coin flips. diff --git a/R/crew_controller.R b/R/crew_controller.R index 3fca91dd..1f2bcf56 100644 --- a/R/crew_controller.R +++ b/R/crew_controller.R @@ -361,6 +361,8 @@ crew_class_controller <- R6::R6Class( }, #' @description Run worker auto-scaling in a private `later` loop #' every `controller$client$seconds_interval` seconds. + #' @details Call `controller$descale()` to terminate the + #' auto-scaling loop. #' @param controllers Not used. Included to ensure the signature is #' compatible with the analogous method of controller groups. #' @return `NULL` (invisibly). @@ -382,6 +384,15 @@ crew_class_controller <- R6::R6Class( invisible() # nocov end }, + #' @description Terminate the auto-scaling loop started by + #' `controller$autoscale()`. + #' @param controllers Not used. Included to ensure the signature is + #' compatible with the analogous method of controller groups. + #' @return `NULL` (invisibly). + descale = function(controllers = NULL) { + private$.autoscaling <- FALSE + invisible() + }, #' @description Push a task to the head of the task list. #' @return Invisibly return the `mirai` object of the pushed task. #' This allows you to interact with the task directly, e.g. diff --git a/R/crew_controller_group.R b/R/crew_controller_group.R index f5b26013..23255a25 100644 --- a/R/crew_controller_group.R +++ b/R/crew_controller_group.R @@ -342,6 +342,15 @@ crew_class_controller_group <- R6::R6Class( walk(control, ~.x$autoscale()) # nocov end }, + #' @description Terminate the auto-scaling loop started by + #' `controller$autoscale()`. + #' @param controllers Not used. Included to ensure the signature is + #' compatible with the analogous method of controller groups. + #' @return `NULL` (invisibly). + descale = function(controllers = NULL) { + control <- private$.select_controllers(controllers) + walk(control, ~.x$descale()) + }, #' @description Push a task to the head of the task list. #' @return Invisibly return the `mirai` object of the pushed task. #' This allows you to interact with the task directly, e.g. diff --git a/man/crew_class_controller.Rd b/man/crew_class_controller.Rd index 18286883..2c73b04c 100644 --- a/man/crew_class_controller.Rd +++ b/man/crew_class_controller.Rd @@ -83,6 +83,7 @@ auto-scaling is currently running} \item \href{#method-crew_class_controller-launch}{\code{crew_class_controller$launch()}} \item \href{#method-crew_class_controller-scale}{\code{crew_class_controller$scale()}} \item \href{#method-crew_class_controller-autoscale}{\code{crew_class_controller$autoscale()}} +\item \href{#method-crew_class_controller-descale}{\code{crew_class_controller$descale()}} \item \href{#method-crew_class_controller-push}{\code{crew_class_controller$push()}} \item \href{#method-crew_class_controller-walk}{\code{crew_class_controller$walk()}} \item \href{#method-crew_class_controller-map}{\code{crew_class_controller$map()}} @@ -444,6 +445,33 @@ every \code{controller$client$seconds_interval} seconds. \if{html}{\out{
}}\preformatted{crew_class_controller$autoscale(controllers = NULL)}\if{html}{\out{
}} } +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{controllers}}{Not used. Included to ensure the signature is +compatible with the analogous method of controller groups.} +} +\if{html}{\out{
}} +} +\subsection{Details}{ +Call \code{controller$descale()} to terminate the +auto-scaling loop. +} + +\subsection{Returns}{ +\code{NULL} (invisibly). +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-crew_class_controller-descale}{}}} +\subsection{Method \code{descale()}}{ +Terminate the auto-scaling loop started by +\code{controller$autoscale()}. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{crew_class_controller$descale(controllers = NULL)}\if{html}{\out{
}} +} + \subsection{Arguments}{ \if{html}{\out{
}} \describe{ diff --git a/man/crew_class_controller_group.Rd b/man/crew_class_controller_group.Rd index 2c84b3af..3786a78d 100644 --- a/man/crew_class_controller_group.Rd +++ b/man/crew_class_controller_group.Rd @@ -73,6 +73,7 @@ condition variable.} \item \href{#method-crew_class_controller_group-launch}{\code{crew_class_controller_group$launch()}} \item \href{#method-crew_class_controller_group-scale}{\code{crew_class_controller_group$scale()}} \item \href{#method-crew_class_controller_group-autoscale}{\code{crew_class_controller_group$autoscale()}} +\item \href{#method-crew_class_controller_group-descale}{\code{crew_class_controller_group$descale()}} \item \href{#method-crew_class_controller_group-push}{\code{crew_class_controller_group$push()}} \item \href{#method-crew_class_controller_group-walk}{\code{crew_class_controller_group$walk()}} \item \href{#method-crew_class_controller_group-map}{\code{crew_class_controller_group$map()}} @@ -418,6 +419,28 @@ every \code{controller$client$seconds_interval} seconds. \if{html}{\out{
}}\preformatted{crew_class_controller_group$autoscale(controllers = NULL)}\if{html}{\out{
}} } +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{controllers}}{Not used. Included to ensure the signature is +compatible with the analogous method of controller groups.} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +\code{NULL} (invisibly). +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-crew_class_controller_group-descale}{}}} +\subsection{Method \code{descale()}}{ +Terminate the auto-scaling loop started by +\code{controller$autoscale()}. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{crew_class_controller_group$descale(controllers = NULL)}\if{html}{\out{
}} +} + \subsection{Arguments}{ \if{html}{\out{
}} \describe{ diff --git a/tests/interactive/test-promises-groups.R b/tests/interactive/test-promises-groups.R index d4a7481d..e2c3f997 100644 --- a/tests/interactive/test-promises-groups.R +++ b/tests/interactive/test-promises-groups.R @@ -1,5 +1,5 @@ # {later} async has a hard time inside functions that do not relinquish -# the main event loop, so these tests need to run interactively. +# the main event loop, so these tests need to run interactively line-by-line. crew_test("interactive: promise(mode = \"one\") on controller groups", { a <- crew_controller_local( name = "a", @@ -104,5 +104,8 @@ crew_test("interactive: promise(mode = \"all\") on controller groups", { x$wait(mode = "all") expect_equal(envir$error, "error message") expect_null(envir$value) + expect_true(a$autoscaling) + x$descale() + expect_false(a$autoscaling) x$terminate() }) diff --git a/tests/interactive/test-promises.R b/tests/interactive/test-promises.R index c14890c7..c961ac8d 100644 --- a/tests/interactive/test-promises.R +++ b/tests/interactive/test-promises.R @@ -1,5 +1,5 @@ # {later} async has a hard time inside functions that do not relinquish -# the main event loop, so these tests need to run interactively. +# the main event loop, so these tests need to run interactively line-by-line. crew_test("interactive: promise(mode = \"one\") on basic controllers", { x <- crew_controller_local( workers = 1L, @@ -51,6 +51,7 @@ crew_test("interactive: promise(mode = \"all\") on basic controllers", { ) x$start() x$push("done1") + Sys.sleep(0.1) x$push("done2") envir <- new.env(parent = emptyenv()) # Test on good tasks. @@ -87,5 +88,8 @@ crew_test("interactive: promise(mode = \"all\") on basic controllers", { x$wait(mode = "all") expect_equal(envir$error, "error message") expect_null(envir$value) + expect_true(x$autoscaling) + x$descale() + expect_false(x$autoscaling) x$terminate() }) diff --git a/tests/testthat/test-crew_controller.R b/tests/testthat/test-crew_controller.R index 824848b9..4d0eb366 100644 --- a/tests/testthat/test-crew_controller.R +++ b/tests/testthat/test-crew_controller.R @@ -428,3 +428,10 @@ crew_test("backlog with saturation", { x$push(Sys.sleep(30), scale = FALSE) } }) + +crew_test("descale", { + controller <- crew_controller_local() + expect_null(controller$autoscaling) + controller$descale() + expect_false(controller$autoscaling) +}) diff --git a/tests/testthat/test-crew_controller_group.R b/tests/testthat/test-crew_controller_group.R index 3f4a5dfc..4c19f223 100644 --- a/tests/testthat/test-crew_controller_group.R +++ b/tests/testthat/test-crew_controller_group.R @@ -723,3 +723,11 @@ crew_test("group helper methods (non)empty, (un)resolved, unpopped", { expect_equal(x$unpopped(), 0L) x$terminate() }) + +crew_test("descale", { + controller <- crew_controller_local() + x <- crew_controller_group(controller) + expect_null(controller$autoscaling) + x$descale() + expect_false(controller$autoscaling) +})