Datastore caching is fundamentally broken because the cache time is set on every successful request, even if that request returns the cached value.
In short, this means that a loop with GetAsync with an interval that is shorter than the cache expiration time (= 4 seconds) can cause a key to be forever locked into its old value, unless updated on the server where this loop is running.
How to reproduce:
- Open a new baseplate
- Publish the baseplate to your profile, a place with Studio API access
- Play the game
- Run this in the developer console server-side:
local data = game:GetService("DataStoreService"):GetDataStore("Test")
data:SetAsync("TestKey", "Default")
local oldValue = "Default"
while wait(1) do -- less than 4 second cache cooldown
local value = data:GetAsync("TestKey")
if value ~= oldValue then
print("changed to:", value)
oldValue = value
end
end
- Go back to Studio, and run this code after a few seconds in the command bar:
local data = game:GetService("DataStoreService"):GetDataStore("Test")
data:SetAsync("TestKey", "Another Value")
Observed behavior:
In the live instance, nothing appears in the output.
Why is this unexpected:
This is unexpected, because I updated the value through Studio. This is because of the issue I described in the first paragraph of this thread. Actually, if you run the following code (with an interval of >4 seconds) instead and repeat the steps, you will see that the change is registered correctly:
local data = game:GetService("DataStoreService"):GetDataStore("Test")
data:SetAsync("TestKey", "Default")
local oldValue = "Default"
while wait(5) do -- >= 4 second cache cooldown
local value = data:GetAsync("TestKey")
if value ~= oldValue then
print("changed to:", value)
oldValue = value
end
end
This should also occur with the wait(1) loop, but it doesn’t, for reasons described earlier in the thread.
Expected behavior:
The value I updated through Studio should be registered, no matter how tight the interval of the loop is. The live instance should always be able to check for new values 4 seconds after the latest value was fetched from the remote data services.
How to fix this issue:
It’s simple: don’t set the cache time again when the current request is a cache hit. Only set the cache time when a remote call was performed. This way, if you have a loop with GetAsync, it will consume budget and perform a remote call once per 4 seconds (= the cache expiration time), rather than getting stuck at the old value forever.
In short: make sure the cache can be busted even in a tighter loop than the 4 second cache time interval.