Skip to content

An edited or never-run block that is off screen reads dormant, not stale #362

Description

@nbenn

A block that is not needed reports dormant in three situations the status cannot tell apart: its last evaluation is still current, its own state changed after that evaluation, or it has never been evaluated. Only an upstream change turns it stale (#310). A consumer that reads dormant as "up to date" is wrong in the second and third case, and the block's descendants inherit the error, since a dormant upstream counts as fine.

Repro

Against main at 366150a, with only data on screen:

library(shiny)
library(blockr.core)

options(blockr.background_construction_delay = 0)

board <- new_board(
  blocks = c(
    data = new_dataset_block("iris"),
    a = new_subset_block(subset = "Sepal.Length > 5"),
    b = new_head_block()
  ),
  links = c(new_link("data", "a", "data"), new_link("a", "b", "data"))
)

testServer(
  get_s3_method("board_server", board),
  {
    report <- function(step) {
      status <- vapply(c("a", "b"), function(id) reval_if(rv$eval[[id]]), "")
      res <- rv$blocks$a$server$result()
      cat(
        format(step, width = 30), paste(names(status), status, sep = "=", collapse = " "),
        " a$result():", if (is.null(res)) "NULL" else paste(dim(res), collapse = "x"), "\n"
      )
    }

    show <- function(on) {
      for (id in c("a", "b")) {
        vis$required[[id]](on)
        vis$visible[[id]](on)
      }
      session$flushReact()
    }

    session$flushReact()
    report("never shown")

    show(TRUE)
    report("on screen")

    show(FALSE)
    report("off screen again")

    board_update(list(blocks = list(mod = list(a = list(subset = "Sepal.Length > 7")))))
    session$flushReact()
    report("a's own subset edited")

    board_update(list(blocks = list(mod = list(data = list(dataset = "mtcars")))))
    session$flushReact()
    report("upstream dataset changed")
  },
  args = list(
    x = board,
    callbacks = function(visibility, ...) {
      visibility$required[["data"]](TRUE)
      visibility$visible[["data"]](TRUE)
    }
  )
)
never shown                    a=dormant b=dormant  a$result(): NULL
on screen                      a=ready b=ready  a$result(): 118x5
off screen again               a=dormant b=dormant  a$result(): NULL
a's own subset edited          a=dormant b=dormant  a$result(): NULL
upstream dataset changed       a=stale b=stale  a$result(): NULL

After a's subset is edited off screen, both a and b are out of date and both read dormant. The first line is the never-run case, which reads the same. (The NULL results are a separate gap, filed next to this one.)

Cause

The input_stale reactive (block-server.R#L323-L375) compares only what each upstream produced against what the block consumed from it. It never looks at the block's own expression, and it returns FALSE outright when the block has no previous evaluation (#L344-L346). Issue #318 recorded the first gap ("a change to the block's own expression is not staleness") and answered it with the evaluate and sustain requests. Those let a consumer force a run, but not tell which blocks need one.

Suggested direction

Extend input_stale to the block's own side of the key the unchanged-inputs skip already uses (#L412-L418): compare the current expression against the one in last_eval by the same identity test, and the eval trigger by value, as the skip does. An own edit then reads stale, and the existing upstream check carries it down the cone. A block with no previous evaluation gets a status of its own, say unevaluated, which propagates downstream like stale does. With that, dormant means one thing: not evaluated now, and nothing it depends on has changed since it was.

The immediate consumer is the assistant's commit review (BristolMyersSquibb/blockr.assistant#165). It needs to know whether a change it just made broke a block the user cannot see. With these statuses it would request a one-off evaluate for whatever reads stale or unevaluated, wait until none does, and read the rest as current. Today a one-off run ends in dormant, which cannot be told from never having run, so it holds a sustain claim over the edited blocks instead (BristolMyersSquibb/blockr.assistant#164).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions