This is a continuation of the previous blog post, How Sneaker Bots Work Part 1.
When people first started using bots to automate sneaker purchasing, it was relatively easy to guarantee a successful purchase with only a couple running "tasks". Tasks are the individual processes that a bot runs to purchase a sneaker. As the sneaker bot market grew, so did the competition. Sneaker sites started setting up queues where people would wait for their chance to buy shoes. This led to people running more tasks to increase their chances of getting past queues.
When I was still working on Project Destroyer, I eventually ran into the scaling task problem. Users wanted to run hundreds of thousands of tasks, but the bot was not built to handle that many. In this blog, I will discuss how I scaled Project Destroyer to run thousands of tasks at once.
Project Destroyer was built using Electron to create a desktop application. It was easy to pair a React.js frontend with a Node.js backend to handle logic, using Electron features like IPC.
The image above is what Project Destroyer looked like. Each row in the table was an independent task that would try to buy sneakers from "Mondo Shop" by searching for products with keywords like "coco", "soundtrack", etc using the inputted accounts and proxies.
Since Project Destroyer's backend was Node.js, it was hard to efficiently run hundreds of tasks "concurrently" without taxing the computer. There were lots of synchronous bottlenecks happening with the request client I built that uses Electron. Therefore, I decided it would be better to move the task logic to a more efficient langauge known for concurrency, like Golang.
Go had a couple of advantages that made it a good choice for sneaker bots. First, Go has goroutines. Goroutines are threads that are managed by the Go runtime. They are easy and fast to create and have low overhead. Sounds perfect for running hundreds of sneaker bot tasks. All I had to do was create logic that would run a goroutine for each task and manage them. Easy.
Second, Go has a great standard request library that can be modified to make requests that aren't fingerprinted by sneaker sites. All that had to be done was fork the utls library to update the TLS fingerprinting by adding more HelloChromes and correct header order.
In essence, Go was the perfect language to use. Instead of talking about how I wrote it in this post, I made a small github repo of how I would run tasks concurrently in Go if you were interested. I also added optimizations like caching request clients so that they wouldn't have to be created for each task.
However, the frontend was still in React.js and the backend was in Node.js. I had to figure out how to communicate between the two.
A method that I first tried using was setting up gRPC remote calls between Go and Node.js. It was a good idea in theory and probably works much better in other real world applications. It seemed like gRPC was what I needed to communicate between the two languages efficiently. However, for Project Destroyer, gRPC seemed too slow and overkill in practice (I could've also set it up incorrectly but that was a couple years ago). There was too much latency between communications, which caused a lot of lag and memory issues.
An easier method was just to create a simple REST API in Go that Node.js could call to start tasks and get task updates. Any Go REST library would do. Go can handle a lot of requests at once, so it was perfect for this. After setting up the REST API, running hundreds of tasks seemed much smoother than before. Memory and CPU usage was much lower and the tasks were running much faster.
To conclude, Project Destroyer was originally built with just a Node.js backend and React.js frontend. However, I eventually scaled Project Destroyer to use Go for its backend task system. To communicate between Node.js and Go, instead of using something like gRPC or websockets, I just set up a simple REST API in Go. This allowed me to run hundreds of tasks concurrently with minimal CPU and ram usage.
Scaling Project Destroyer was a fun experience and taught me a lot. It's always fun to look back on old projects to see how much I've learned since then from classes or other experiences.
Thanks for reading ❤️
- Nate