Running a "cron-job" to update a value in every server

Say I have a shop in my game & I want it to be the same items in the shop globally across every server, however I also want this shop to auto-refresh with new items every 60 minutes… How could I accomplish this?

My first thought was that I could just scan a data-store every 1-3 minutes and check the last time the shop was updated and then update it if it hasn’t been updated yet, however what happens if another server was checking it at the exact same time? Data Stores are known to cache and give inaccurate information at times, so the shop would be updated twice in that case & I’m sure people would be sticking around to see the new items in the shop as soon as the hour hits. They would see a bunch of items swapping back and forth and would be able to buy them all if multiple servers attempted to update the shop all at the same time.

I’ve heard that MessagingService is coming out soon, however unless this can instantaneously send and receive data cross-server I don’t see this fixing the problem either. Any help would be appreciated, thanks. :slight_smile:

Please note: I do not want to use an external server for this.

Polling data stores in long intervals like that may result in desynchronisation or highly inaccurate data, because now your servers all have different reference points. MessagingService is definitely something to wait for, as it is designed for cross-server communication.

Nothing you do is ever really “instantaneous”, however it’s the closest you’ll be able to get to live updates per server. I would definitely suggest usage of the MessagingService and running some experiments to get a sense of what it’s what you seek or not. The backend will be enabled soon, so you can start working on implementing it on the frontend now.

Why? This statement signals the flare to the beginning of an XY problem. What would you do if external servers were the only way to get it done without slowly polling DataStores inaccurately? Without MessagingService, this is the case.

You’ve left out a critical detail: what determines which items are in each update? If the items can be chosen by PRNG, you can quantize os.time() to hour resolution, and use it to seed Random.new() each time it increments, thereby making the whole thing deterministic and not requiring any server-to-server communication. Each server would independently come up with the same result.

If the shop data isn’t procedurally generated, this isn’t useful. That’s why it’s an important detail.

7 Likes

Why couldn’t you just have the items in an external module and then do the require inside the function that fires every 60 minutes. So that all you need to do is update the table in the module and then all servers are referencing that module?

Assuming you are retrieving data and not writing data this is perfectly fine. If you needed to, you could be checking for data every 10 seconds, and still easily be within your (60 + numPlayers × 10) request limit.

Your main area for concern is the method in which to write new items to this datastore. If writing from multiple servers, then yes, the issue you mentioned would be a problem, otherwise achieving this through a single-source, such as in studio, is fine.

Out of interest, how do you plan to write the new items into the shop?

Because require caches for external modules too.

Vesteria uses both of these API in conjunction to fetch a modulescript with up-to-date game configuration data.

You fetch the latest asset version every 5 seconds or so, and if its newer than what the game server already has then you load it in and overwrite the version you have. Very fast and reliable.

edit: I just saw you don’t want to use external APIs (implication would be you have some sort of bot to update the shop, but at that point you may as well just return a raw json with the external service assuming it can handle the load of a ton of game servers pinging it, which if it can’t you’d have it update a model on roblox and do what I suggested previously)… Your best bet would be to do as someone else recommended and generate a shop list based on a random object with os.time to the nearest hour. It’ll be synced across servers and update automatically.

MessagingService won’t be a good fit because you’ll need to decide which server is authoritative over signaling new shop refreshes which, good luck.

4 Likes

I intended to just do math.random(#shopItems) to select the item(s) that it would add to the shop. How exactly would I make this return the same thing for every server using os.time() converted an hour format?

In a server update loop that is called periodically, you’d have some code like:

currentHour = math.floor(os.time() / 3600))
if currentHour  > lastHour then
    local rng = Random.new(math.floor(os.time() /3600))
    -- Do calls to rng:NextInteger() or whatever you need to generate the data
    currentHour = lastHour
end

Where currentHour and lastHour are declared outside the update loop, so they persist. Unlike math.random(), using an instance of Random with an integer seed (other than 0) will get you a deterministic stream of numbers that will be the same on all platforms. The important bit is that each server uses the same seed. os.time() will be very close across different servers, they should normally be within a few seconds of agreement with each other (though in some cases a server can be minutes off, but this is rare).

1 Like