Hello,
I'm developing several Shiny apps where users can create accounts and generate/upload data for analysis. The data is always of specific structure and I store data for all users in tables. So far I've followed the general advice that as long as the data fits in memory than don't create a database. I simply store data frames as Rds files.
Currently I use the following structure in Apps.
- I use reactive values in the server to store if a user is logged in or not.
- I use reactive values to store the data
- Read and write the database when data is updated.
The server logic has these elements:
shinyServer(function(input, output, session) {
# Initialise databases
rv_db <- reactiveValues(
user = readRDS("input/user.Rds"),
data1 = readRDS("input/data1.Rds"))
# User login
rv_user <- reactiveValues(
user_id = NULL,
user_name = NULL)
# Add user generated data
observeEvent(input$add_data_button, {
# Read data (in case it was changed by another user)
rv_db$data1 = readRDS("input/data1.Rds")
# Add user generated data
rv_db$data1 = bind_rows(rv_db$data1, newly_generated_data)
# Store changes in the file
saveRDS(rv_db$data1, "input/data1.Rds")
})
})
My actual current apps are a little more complex, but this is the core idea.
I like this approach because:
- it seems to work
- I can use refresh buttons, tab-changes or other triggers to read the data again. In case there were changes by other users overview analysis/graphs can respond to it.
But I have some issues with this approach.
The data is now loaded separately for each user and stored in that session's reactive value. That means that if multiple users are using the app it quickly fills up the memory with unnecessary duplicates. (Can someone confirm that that happens?)
Also, what happens if two users add data add the same time? I'm not sure how Shiny handles the order of computations. Imagine that the step to add newly generated data to the previous data takes time, so there is significant time span between read and write data. What if two people do that at the same time. Does Shiny first handle the observeEvent({}) for 1 session and then for the other. Or is it possible that they both read the data simultaneously and the session to write the data last overwrites and deletes the data written by the other session.
I'd love to see what others use as solutions for a Shiny app with users and persistant data storage. I'm also happy to push some ideas to github and collaborate on 'skeleton' repo for this kind of app.
Best,
Jiddu