yral-dapp / hot-or-not-backend-canister

Other
9 stars 6 forks source link

Hot or Not Game Backend #331

Open ravi-sawlani-yral opened 4 months ago

ravi-sawlani-yral commented 4 months ago

backend

abhishek-tripathi-yral commented 4 months ago

Here are the findings for the approach for infinite slots in hot or not game.

DATA structures:

// system time is the time when first best placed.
map = BTreeMap<PostId, SystemTime>  

queue = Vec<PostId> 

both BTreeMap and Vec are from ic_stable_structures

Here is the flow for a bet.

  1. when user places a bet a) check if the post_id exists in map. b) if it does, ignore. c) if it does not, put post_id in map and push it at end of queue.

Current algo for betting slot on a post starts the slot after the first bet comes. So, the slot for each post is dynamic. Slot starts right after the first bet comes and slot lasts for 60 minutes. Slots are not linked to post created_at time to ensure the fairness in the game.

  1. Timers on the bet a) When user places a bet, we run the timer if not running already.

b) For a canister, with zero posts in queue => When the first bet comes, the timer is started and relevant changes are made to queue and map. the timer is stored in a global_timer variable.

c) For canister with some posts in queue => new bet comes => Check if the global_timer is running (i.e. has Some(TimerId)). If it is running, don't start a new timer. Just add the bet in map and queue.

d) when the old timer finishes, it evaluates the map and queue to setup another timer.

abhishek-tripathi-yral commented 4 months ago

findings 2024-07-09

preferable choice:

  1. resize slot_ids from u8 to u32
abhishek-tripathi-yral commented 4 months ago

https://github.com/TwistingTwists/test_u8_upgrade

Update: 2024-07-12

abhishek-tripathi-yral commented 4 months ago

Here is a list of sub-tasks for the game. Will check them as we progress.

harshita-srivastava-yral commented 4 months ago
harshita-srivastava-yral commented 4 months ago
harshita-srivastava-yral commented 4 months ago

@abeeshake-yral Please comment on the timeline for UAT and delay expected in go-live

harshita-srivastava-yral commented 3 months ago

Expected to be finished today in first half!

Natasha-GB commented 3 months ago
harshita-srivastava-yral commented 3 months ago

-Ensure backward compatibility of the end points

abhishek-tripathi-yral commented 3 months ago

UPDATED : https://github.com/go-bazzinga/hot-or-not-backend-canister/issues/341#issuecomment-2251905756

Backwards compatiblity todos:

  1. did files changed. Approved by Ravi. vec nat8 -> blob
  2. nat64 for slot_id in frontend structs : https://github.com/go-bazzinga/hot-or-not-backend-canister/pull/330/files#r1691183127
Natasha-GB commented 3 months ago
abhishek-tripathi-yral commented 3 months ago

First PR is up : https://github.com/go-bazzinga/hot-or-not-backend-canister/pull/352

Second PR is in progress. Will be raised today.

BBC - 3. in progress. expected to be done by tomorrow.

abhishek-tripathi-yral commented 3 months ago

Second PR is up: #354

With second PR , Here is what is finished

  1. frontend and backwards compatibility
  2. integration tests are in place.

While this is being reviewed, I will move on to finishing frontend tasks.

harshita-srivastava-yral commented 3 months ago
Natasha-GB commented 3 months ago
abhishek-tripathi-yral commented 3 months ago

Betting starts when the first user bets on a given post. Betting slot is alloted thena and a timer for 1 hr is started.

Here are two apporaches to implement the timer:

  1. have a timer for each post which is being betted on
  2. have a global timer and a queue of posts that are being betted on.
    1. iterate over the queue and check if the timer has expired.
    2. if the timer has expired, then tabulate the outcome for the post and remove the post from the queue.

We chose the second approach because it is more efficient.

Here are the scenarios that can happen for the timers:

post_2_Z => means post 2 for the user with id Z

  1. User A bets on a post_1_Z at 04:00 and the global timer for 1 hr is started (to be finished at 05:00)
  2. User B bets on a post_1_Z at 04:01 and no timer is started. (since there should be ONLY one timer in the canister at a time)
  3. User C bets on a post_2_Z at 04:02 and no timer is started. (since there should be ONLY one timer in the canister at a time)

When timer expires, post_1_Z is tabulate_hot_or_not_outcome_for_post_slot_v1 and post_1_Z is removed from the queue. Next timer is queued for the next post (post_2_Z) in queue.

Note that this second timer actually needs to run only for 1 minute since this timer is started at 05:00 and the expiry of this timer needs to be at 05:01. Computationally, the function tabulate_hot_or_not_outcome_for_post_slot_v1 after 1 hr for post_2_Z.

Here are the technical details for implementing the timer:

  1. queue is best implemented in stable memory as ic_stable_structures::btree_map::BTreeMap because it has pop_first and insert methods. The Vec in ic_stable_structures doesn't have pop_first.
  2. we also need to keep track of the time when the first bet was placed for each post. This is because timer expires 1 hr after the first bet on the given post is placed.
    pub bet_timer_posts: ic_stable_structures::btreemap::BTreeMap<(SystemTimeInMs, PostId), (), Memory>,

Here are the scenarios that can happen for the timers:

Think of these sequentially.

A1. when first bet on post_1_Z is placed, and bet_timer_posts is empty. We insert into bet_timer_posts a tuple of (SystemTimeInMs, PostId) and start_timer.

A2. when second bet on post_1_Z is placed, we check if PostId(post_1_Z) exists in bet_timer_posts. if it doesn't, then we insert into bet_timer_posts a tuple of (SystemTimeInMs, PostId). we don't ned to call start_timer here. Since there must be a global timer running. It was set in A1.

A3. when first bet on post_2_Z is placed, we check if PostId(post_2_Z) exists in bet_timer_posts. It doesn't. So, we insert into bet_timer_posts a tuple of (SystemTimeInMs, PostId). we don't ned to call start_timer here.

A4. when timer for post_1_Z expires, we tabulate_hot_or_not_outcome_for_post_slot_v1 for post_1_Z and remove it from bet_timer_posts. also, start the next timer (via start_timer) for the next post (post_2_Z) in the queue bet_timer_posts.

Now, in A2 above, to check if the posts has already been betted upon, in O(1) time, we need a separate BTreeMap<PostId, ()>. we call this map first_bet_placed_at_hashmap. now this map needs to be updated always along with bet_timer_posts. at the cost of memory, we are optimising for lookup speed.

originally posted : https://github.com/go-bazzinga/hot-or-not-backend-canister/pull/354#issuecomment-2265673560

Natasha-GB commented 3 months ago
abhishek-tripathi-yral commented 2 months ago

Komal reviewed the PR. Suggested a way to merge PR in one step. Testing that right now.

Natasha-GB commented 2 months ago
harshita-srivastava-yral commented 2 months ago
harshita-srivastava-yral commented 2 months ago
abhishek-tripathi-yral commented 2 months ago

Blocked on call exceeded issue on my laptop. Will try on Adarsh's laptop

Next step for this is:

Error on Adarsh's laptop: image

harshita-srivastava-yral commented 2 months ago
harshita-srivastava-yral commented 2 months ago
harshita-srivastava-yral commented 2 months ago