Skip to content

umairansar/SuperStock

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

superstock_retro_transparent_noborder_noarrow

Phase 1 ✅

image

API(s):

  • BuySafe Atomically decrements stock in database.
  • BuyFastAtomic Atomically decrements stock in in-memory cache.
  • BuyFastSignal Non-locking synchronization context to decrementing stock in in-memory cache.
  • BuyFastLocking Exclusive lock while decrementing stock in in-memory cache.

Phase 2 ✅

image

API(s):

  • BuyFastAtomic Atomically decrements stock in Concurrent Dictionary.

Command(s):

  • Navigate to directory SuperStock/Tests in terminal
  • Run k6 run k6.js

Phase 3 🚸✅

image

Redis Pub/Sub Notes

Redis Pub/Sub is stateless which means cache update events could be lost if either publisher, consumer, or Redis are down. Writes will still be consistent, as long as Writer node is up and running. Possibility of oversell in this case, since all the state is entirely maintained in memory.

Oversell Scenario

  • Primary sells some tickets i.e. writes
  • Cache update events fail to be sent by Primary or accepted by Redis or consumer by Secondaries
  • Primary then goes down
  • Readers still have old stock counts for ticket in their cache
  • Last few writes by Primary are forever lost

Possible Solutions

  • Use Write Ahead Log (WAL). Orchestrator synchronizes them during automatic failover. Perhaps we can access WAL for inaccessible Primary node via docker volumes. But that tighly couples Orchestrator with the SuperStock cluster (all 3 nodes). Orchestrator is still a single point of failure. Need to look into leaderless distributed election.

  • Use Quorams. Primary publishes a message and majority nodes reply with success, then Primary commits the transaction i.e. Ticker Sold. But what happens if all replies got Success but Primary went down? Then all the majority nodes would have comitted the sell (I hope...), even though Primary did not commit. In this case, Orchestrator can detect that Primary node died and send TraceId to all Secondary nodes who will revert the stock for the corresponding TraceId. No need to worry about lost updates on Secondary Nodes, since all the subsequent writes after our TraceId are not possible since Primary was already down.

Phase 4

image

Phase 5

image

Phase 6

image

Benchmarks

Phase 1

  • 2686 RPS siege -c5 -t2s -b 'http://localhost:5059/api/v1/OneStock/Db/Buy POST'
  • 7140 RPS siege -c5 -t2s -b 'http://localhost:5059/api/v1/OneStock/Cache/Atomic/Buy POST'
  • 9872 RPS siege -c5 -t2s -b 'http://localhost:5059/api/v1/OneStock/Cache/Signal/Buy POST'
  • 6496 RPS siege -c5 -t2s -b 'http://localhost:5059/api/v1/OneStock/Cache/Lock/Buy POST'

Phase 2

K6 Scipt ensures multiple readers and writers operate on multiple products at the same time. In this scenario, the stock for some popular company shares (NVDA, TSLA, AMD) starts at 15000. Then all concurrent requests buy those and eventually, I ensure that the number of buys are equal the number of sells (incremented atomically on successful buy).

image

Phase 3

Docker Setup

Build and run mongo db container

docker pull mongo
docker run -d --name mongo-for-super-stock -p 27018:27017 mongo

Go to directory containing DockerFile and build:

docker build --no-cache -t couter-image:latest -f Dockerfile .

Then run the container (3 copies)

docker run -d --name core-counter -p 5059:5059 couter-image   
docker run -d --name core-counter-1 -p 5080:5059 couter-image
docker run -d --name core-counter-2 -p 5081:5059 couter-image

Stop and remove container

docker rm -f core-counter

Redis Pub/Sub via Docker

  • Primary instance publishes cache update events
  • Secondary instances consume the message
  • Primary ignores the consumption of echo message
Screenshot 2025-11-01 at 3 43 54 AM

How to start containers with host id passed as environment variable?

docker run -it -e STOCK_HOST_ID=SuperPrimary --name core-counter -p 5059:5059 couter-image
docker run -it -e STOCK_HOST_ID=SuperSecondary --name core-counter-1 -p 5080:5059 couter-image 
docker run -it -e STOCK_HOST_ID=DuperSeconfary --name core-counter-2 -p 5081:5059 couter-image

Random ideas

Here are “bigger-picture” places you’d reach for Interlocked / Volatile—not just code snippets:

Hot-reloadable config / feature flags (snapshot publish) Build a new immutable config object and Exchange it into place; readers Volatile.Read the latest snapshot without locks. (RCU / copy-on-write.)

In-process leader / ownership Competing workers attempt CompareExchange(ref ownerThreadId, myId, 0). The winner becomes the single writer; others act as readers.

Zookeeper? https://zookeeper.apache.org/doc/current/zookeeperOver.html MPSC? https://alexsaveau.dev/blog/opinions/performance/lockness/lockless-queues-are-not-queues

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors