Earlier quoted context omitted.
The short answer is that we tried, back in 2020, while working on a central bank payment switch by the Gates Foundation. We found we were hitting the limits of Amdahl's Law, given Postgres' concurrency control with row locks held across the network as well as internal I/O, leading to the design of TigerBeetle. To specialize not for general purpose but only for transaction processing. On the one hand, yes, you could u…
i am not an exact expert on the limitation you claim to have encountered on postgresql but perhaps someone with more postgresql expertise can chime in on this comment and give some insight
Building a high-performance ticketing system with TigerBeetle
31–40 of 47 posts
Re: Building a high-performance ticketing system with TigerBeetle
#32Earlier quoted context omitted.
Did the company end up using it?
We didn't rule out using Tigerbeetle, but the drop in non-batch performance was disappointing and a reason we haven't prioritised switching our transaction ledger from PostgreSQL to Tigerbeetle. There was also poor Ruby support for Tigerbeetle at the time, but that has improved recently and there is now a (3rd party) Ruby client: https://github.com/antstorm/tigerbeetle-ruby/
Re: Building a high-performance ticketing system with TigerBeetle
#33I recently did performance testing of Tigerbeetle for a financial transactions company. The key thing to understand about Tigerbeetle's speed is that it achieves very high speeds through batching transactions. ---- In our testing: For batch transactions, Tigerbeetle delivered truly impressive speeds: ~250,000 writes/sec. For processing transactions one-by-one individually, we found a large slowdown: ~105 writes/sec.…
> One way to keep those faster speeds in Tigerbeetle for real-time workloads is microbatching incoming real-time transactions to Tigerbeetle at an interval of every second or lower, to take advantage of Tigerbeetle's blazing fast batch processing speeds.
We don’t recommend artificially holding transfers just for batching purposes. René actually had to implement a batching worker API to work around a limitation in Python’s FastAPI, which handled requests per process, and he’s been very clear in suggesting that such would be better reimplemented in Go.
Unlike most connection-oriented database clients, the TigerBeetle client doesn’t use a connection pool, because there’s no concept of a “connection” in TigerBeetle’s VSR protocol.
This means that, although you can create multiple client instances, in practice less is better. You should have a single long-lived client instance per process, shared across tasks, coroutines, or threads (think of a web server handling many concurrent requests).
In such a scenario, the client can efficiently pack multiple events into the same request, while your application logic focuses solely on business-event-oriented chains of transfers. Typically, each business event involves only a handful of transfers, which isn't a problem of underutilization, as they'll be submitted together with other concurrent events as soon as possible.
However, if you’re dealing with a non-concurrent workload, for example, a batch process that bills thousands of customers for their monthly invoices, then you can simply submit all transfers at once.
Re: Building a high-performance ticketing system with TigerBeetle
#34Earlier quoted context omitted.
Yes, that's why I would expect it to smoke Postgres here, in process is orders of magnitude faster. Do you really need concurrency here when you can do 10-100k+ inserts per second?
If 100k users each hit purchase button at the same time will sqlite write it in 1 second? This is different than 1 user doing the purchase for 100k fans
Re: Building a high-performance ticketing system with TigerBeetle
#35Earlier quoted context omitted.
We didn't observe any automatic batching when testing Tigerbeetle with their Go client. I think we initiated a new Go client for every new transaction when benchmarking, which is typically how one uses such a client in app code. This follows with our other complaint: it handles so little you will have to roll a lot of custom logic around it to batch realtime transactions quickly.
> We didn't observe any automatic batching when testing Tigerbeetle with their Go client. This is not accurate. All TigerBeetle's clients also auto batch under the hood, which you can verify from the docs [0] and the source [1], provided your application has at least some concurrency. > I think we initiated a new Go client for every new transaction when benchmarking The docs are careful to warn that you shouldn't be…
Was there something wrong with our test of the individual transactions in our Go script that caused the drop in transaction performance we observed?
Re: Building a high-performance ticketing system with TigerBeetle
#36Earlier quoted context omitted.
> We didn't observe any automatic batching when testing Tigerbeetle with their Go client. This is not accurate. All TigerBeetle's clients also auto batch under the hood, which you can verify from the docs [0] and the source [1], provided your application has at least some concurrency. > I think we initiated a new Go client for every new transaction when benchmarking The docs are careful to warn that you shouldn't be…
Thanks for reaching out. I shared this benchmarking script with your team when we tested Tigerbeetle, but this is it again: https://gist.github.com/KelseyDH/c5cec31519f4420e195114dc9c8... Was there something wrong with our test of the individual transactions in our Go script that caused the drop in transaction performance we observed?
We’d love to roll up our sleeves and help you get it right. Please drop me an email.
Re: Building a high-performance ticketing system with TigerBeetle
#37Having built a ticketing system that sold some Oasis level concerts there's a few misconceptions here: Selling an event out takes a long time to do frequently because tickets are VERY frequently not purchased--they're just reserved and then they fall back into open seating. This is done by true fans, but also frequently by bots run by professional brokers or amateur resellers. And Cloudflare and every other state of…
> Selling an event out takes a long time to do frequently because tickets are VERY frequently not purchased--they're just reserved and then they fall back into open seating. TigerBeetle actually includes native support for "two phase pending transfers" out of the box, to make it easy to coordinate with third party payment systems while users have inventory in their cart: https://docs.tigerbeetle.com/coding/two-phase-…
Re: Building a high-performance ticketing system with TigerBeetle
#38Earlier quoted context omitted.
Thanks for reaching out. I shared this benchmarking script with your team when we tested Tigerbeetle, but this is it again: https://gist.github.com/KelseyDH/c5cec31519f4420e195114dc9c8... Was there something wrong with our test of the individual transactions in our Go script that caused the drop in transaction performance we observed?
Thanks Kelsey! We’d love to roll up our sleeves and help you get it right. Please drop me an email.
Re: Building a high-performance ticketing system with TigerBeetle
#39Earlier quoted context omitted.
> Selling an event out takes a long time to do frequently because tickets are VERY frequently not purchased--they're just reserved and then they fall back into open seating. TigerBeetle actually includes native support for "two phase pending transfers" out of the box, to make it easy to coordinate with third party payment systems while users have inventory in their cart: https://docs.tigerbeetle.com/coding/two-phase-…
It's almost like stored procedures were a good idea.
For example, if you have 8K transactions through 2 accounts, a naive system might read the 2 accounts, update their balances, then write the 2 accounts… for all 8K (!) transactions.
Whereas TB does vectorized concurrency control: read the 2 accounts, update them 8K times, write the 2 accounts.
This is why stored procedures only get you typically about a 10x win, you don’t see the same 1000x as with TB, especially at power law contention.
Re: Building a high-performance ticketing system with TigerBeetle
#40Earlier quoted context omitted.
I'm a bit worried you think instantiating a new client for every request is common practice. If you did that to Postgres or MySQL clients, you would also have degradation in performance. PHP has created mysqli or PDO to deal with this specifically because of the known issues of it being expensive to recreate client connects per request
Ok your comment made me double check our benchmarking script in Go. Can confirm we didn't instantiate a new client with each request. For transparency here's the full Golang benchmarking code and our results if you want to replicate it: https://gist.github.com/KelseyDH/c5cec31519f4420e195114dc9c8... We shared the code with the Tigerbeetle team (who were very nice and responsive btw), and they didn't raise any issues…
For example, it is as if you created an HTTP server that only allows one concurrent request. Or having a queue where only 1 worker will ever do work. Is that your workload? Because I'm not sure I know of many workloads that are completely sync with only 1 worker.
To get a better representation for individual_transfers, I would use a waitgroup
var wg sync.WaitGroup
var mu sync.Mutex
completedCount := 0
for i := 0; i
This will actually allow the client to batch the request internally and be more representative of the workloads you would get. Note, the above is not the same as doing the batching manually yourself. You could call createTransfer concurrently the client in multiple call sites. That would still auto batch them