app.storage
vis-a-vis async concurrency
#5260
Replies: 1 comment
-
Thanks for reaching out @odoublewen. Short answer: yes, the storage is safe from data corruption. Let me explain: The beauty of single-threaded event loop concurrency (like asyncio implements) ensures that only the current task runs between awaits. So any mutation with no await is effectively atomic (for example the dict operations in storage). Beside the in-memory dict the storage mechanism also saves the data to file (or Redis). This backup is done with Mind you, this does not prevent logical async/await errors. For example read-modify-write patterns with await gaps CAN race: count = storage.general.get('count', 0)
await do_work() # Other tasks run here!
storage.general['count'] = count + 1 If two tasks execute this concurrently, both may read For most NiceGUI apps (dashboards, internal tools), this rarely matters. But for high-traffic counters or shared state, you could use async with lock:
count = storage.general.get('count', 0)
await do_work()
storage.general['count'] = count + 1 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
NiceGUI is awesome, and the docs are excellent. Many thanks for the generous contribution to the open source community.
The docs for
app.storage
(https://nicegui.io/documentation/storage) are very good, but there is no mention of async concurrency and whether or not theapp.storage
mechanisms are all "concurrency-safe". I am almost certain they are, but someone at my org is challenging my assumption, and I don't know how to "prove" that they are, so I thought I would ask.(Note, I'm not asking whether the storage mechanisms are thread-safe, like if I decided to somehow run my NiceGUI app with multiple uvicorn workers, which I think is (highly) not recommended. I've read @rodja's post in the r/nicegui thread "Real world scalability" and I get that to safely maintain state one would need to use more sophisticated backend storage like Redis or databases with locking. This question isn't regarding this kind of NiceGUI app deployments.)
And assuming all the
app.storage
mechanisms are "concurrency-safe"... could someone explain how and why? Why isn't it possible that an async coroutine might start writing to, say,app.storage.general
, but then yield to another task that tries to read or write to the same key? I guess the magic is in theObservableDict
andPersistentDict
classes?Beta Was this translation helpful? Give feedback.
All reactions