24

I have a shiny application that queries data from SQL into data frames, and then those data frames are referenced from my shinyServer() block. I've been running it only in RStudio thus far, and so whenever I needed new data I'd just restart the application and before the server loads it would grab all new data.

I'd like to transition the app to shiny server, but I'm not sure how I can induce it to get new data periodically. For the sake of the interface I'd like it to be automatic rather than have a user click a button to initiate the loading. Is there an idiomatic solution for this?

EDIT:

I think I found a solution that works for me.

shinyServer(function(input,output,session){
    sourceData <- reactive({
        invalidateLater(1000000,session)

        functionThatGetsData()
    })
})
Patrick McCarthy
  • 2,478
  • 2
  • 24
  • 40
  • 2
    Hi Patrick. I am also building an app that queries and SQL database. I'm having trouble manipulating the data when I use reactiveValues. Can you send me a link to your app / code or is it sensitive? No problem if it is. Thanks. Pete – Pete900 Jan 06 '16 at 13:05

3 Answers3

28

The smartest would probable be to use reactivePoll if you can make a fast query to detect if there is new data. This worked very well for me just today actually.

reactivePoll shiny

Reactive polling



Description

Used to create a reactive data source, which works by periodically polling a non-reactive data ource.

Usage

reactivePoll(intervalMillis, session, checkFunc, valueFunc)

Arguments

intervalMillis

Approximate number of milliseconds to wait between calls to checkFunc. his an be either a numeric value, or a function that returns a numeric value.

session

The user session to associate this file reader with, or NULL if none. If non-null, he reader will automatically stop when the session ends.

checkFunc A relatively cheap function whose values over time will be tested for equality; nequality indicates that the underlying value has changed and needs to be invalidated and re-ead using valueFunc. See Details. valueFunc

A function that calculates the underlying value. See Details.

PeterVermont
  • 1,922
  • 23
  • 18
Jan Stanstrup
  • 1,152
  • 11
  • 28
  • 1
    I think this is more appropriate (and elegant) than `invalidateLater` as this is exactly designed for OP's problem. – Boxuan Oct 09 '15 at 15:27
  • 2
    Here is a working example for this answer: http://shiny.rstudio.com/gallery/reactive-poll-and-file-reader.html – Boxuan Oct 09 '15 at 15:28
10

You're looking for invalidateLater. Put this, with the appropriate interval, in the reactive expression that retrieves data from the the database.

Matthew Plourde
  • 43,932
  • 7
  • 96
  • 113
  • 2
    Can this live in an observe() outside shinyServer along with a reactive function? It's the sort of thing I'd want to be available to all users. – Patrick McCarthy Jul 11 '14 at 22:20
  • @PatrickMcCarthy you can add `invalidateLater` without `session` in it. also use `<<-` to save it into a global variable for all sessions to use – Pork Chop Apr 03 '17 at 06:52
2

I don't think either of these answers fully answers OP's question. Specifically

whenever I needed new data I'd just restart the application and before the server loads it would grab all new data.

which suggests that the global object needs to be refreshed. Using reactives inside the server function is not a great answer to that problem bc you have a race condition if multiple users are interacting with the app simultaneously ... all of them will try to update the object concurrently, which may or may not resolve itself well depending on what the update is (this is something I'm actively wrestling with).

One possible solution I've explored is using the flock package to have each user attempt to acquire a lock on a global temp file, and the user successfully acquiring it would be tasked with doing the global update. This directly addresses the race condition. I haven't tested it fully enough to post it as a solution here, maybe someone else can run with that.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Matt Anthony
  • 121
  • 8