Skip to content

shinyasync 0.3.0

Choose a tag to compare

@dawiegriesel dawiegriesel released this 20 Oct 08:30
· 21 commits to main since this release

shinyasync 0.3.0

  • The default pool size respects the CPU limits of the process's cgroup. It
    was one less than parallel::detectCores(), which reports the host's
    cores, so in a container or on Posit Connect with a CPU quota or cpuset
    the default pool started a daemon per host core. It is now one less than
    the host's cores capped by the tightest cgroups v1 or v2 quota or cpuset
    on the process or any ancestor cgroup (#17).
  • New vignette, vignette("extendedtask"), writes the surf forecast app with
    ExtendedTask and with shinyasync side by side, one piece at a time: the
    pool, a fetch from an input, status and the previous value, a button with
    cancel, superseded inputs, a chained task, side effects and fan-out, and
    what the layer costs against ExtendedTask.
  • A running task no longer polls mirai::unresolved() every 100 ms. The poll
    covered a promise notification that mirai can lose: its as.promise()
    method checks unresolved() and then registers the notification, and a
    task that completes in between never notifies. One check after
    registration closes that window, so a task settles as soon as its result
    arrives, with no timer. In shiny::testServer(), whose flush ends by
    running due later callbacks, a task that finishes within the flush that
    dispatched it now settles in that flush, where the poll left 100 ms of
    grace; a test that reads the status or value of a quick task straight
    after setInputs() should give the task some work. A test reproduces the
    lost notification by widening the window. The race was fixed upstream in
    nanonext 1.10.3.9000 (r-lib/mirai#656, r-lib/nanonext#363); the check is
    skipped on that version and later, and goes once it is required (#16).
  • A connection opened in pool_async(everywhere = ) can be closed by a
    .Last function assigned in the same expression. R runs it when the
    worker exits, on pool shutdown and when the app process dies, and the pool
    does not wait for it. This is base R, so no teardown argument was added;
    it is documented and tested (#25).
  • pool_async() gains everywhere, an expression evaluated once on every
    worker when the pool starts, for data frames, models and connections that
    every task needs. Objects it refers to are sent once and packages it uses
    are attached; the names it creates are resident, so tasks no longer ship
    them, and print() lists them on a resident line. The README documents
    that shipped objects are otherwise serialized on every run (#15).
  • reactiveValAsync() is labelled with the name it is assigned to, as
    shiny::reactiveVal() is, so values can be told apart in reactlog (#21).
  • A task on a named mirai compute profile (mirai_async(.compute = "x")) no
    longer starts the default pool (#20).
  • pool_async() documents what holds the pool outside an app: a script or
    console call releases it when the next app in the process stops, or at
    process exit (#19).
  • pool_async() called in server() and the first task in that session now
    hold the pool once rather than twice; the counts balanced at session end
    before, so this is an accounting change only (#18).
  • A reactiveAsync() waiting for an upstream async reactive now reports
    "running" while that upstream runs, so a status-driven spinner shows
    during the whole refresh, and in mode = "latest" a run already computing
    on the old upstream value is dropped instead of occupying a worker for a
    result that would be discarded (#14).
  • A downstream gated with bindEvent() whose event fires while its upstream
    async reactive is busy, or before it has started, now runs once the upstream
    settles; cancel_async() drops the pending event, and so does an upstream
    that stops without producing. Previously the event was always lost: the
    halt inside bindEvent()'s isolate() registered no dependency, so
    nothing re-ran the task (#11).
  • A name assigned inside an async expression now shadows the session object
    only after its assignment, so data <- data() reads the reactive and ships
    nothing. Previously the whole expression treated data as local, the
    worker called utils::data() and the result was silently wrong, or failed
    with "could not find function" for other names (#12).
  • mirai (>= 2.5.0) is now required. The code has used mirai::daemons_set()
    (mirai 2.3.0) and the condition.class attribute on worker errors (mirai
    2.5.0) since 0.2.0; with an older mirai a worker-side req() became a real
    error instead of a silent halt (#13).