Live data from Hacker News

How not to structure database-backed web apps: performance bugs in the wild

blog.acolyer.org

201–210 of 319 posts

Re: How not to structure database-backed web apps: performance bugs in the wild

#201

I need a very good reason before using any external library in an attempt to keep the total code base as clean as possible. It is just too easy to be rushed and bring in a heap of code, so I prefer to use SQL instead of ORM's. All access to the database is done in a single module and they are wrapped in functions like below def get_table_as_list(user_id, cols, tbl, where_clause, params_as_list, conn_str, order_by="1"…

It's nice to see that people are still able to write 2002-era PHP in Python today.

Re: How not to structure database-backed web apps: performance bugs in the wild

#202
post #200
post #22

Earlier quoted context omitted.

Writing SQL by hand does not solve typical performance problems like n+1 queries. If on the other hand you know enough to avoid n+1 queries, then you can also avoid them when using an ORM, and save a lot of work. If you like writing SQL by hand, by all means do so, but you will not automatically get better performance by handwritten SQL as compared to ORM generated SQL.

Sure, writing SQL won't solve design problems, but some ORMs need multiple queries to deliver data with relationships, joins, etc, that could be done in a single SQL query.

What ORM's does not support joins?

Re: How not to structure database-backed web apps: performance bugs in the wild

#203

Pretty cool to read. We built a (currently proprietary) CMS with our own scripting language, and instead of going the ORM way, we merged basic SQL into the language itself. We did that mostly to eliminate sending raw strings to databases (and all the injection risks and complexity that comes with it) but it does allow a few extra optimisations because the compiler can look at both the query and the language using it.…

Of course it depends on the exact circumstances, but your query would probably be faster as `SELECT COUNT(*)` than the `LIMIT 1` you optimize to now. In fact, that’s one of the specific optimizations mentioned in the article.

Re: How not to structure database-backed web apps: performance bugs in the wild

#204
post #10

I never understood why I don't enjoy working with ORM but reading this article make it clearer for me. They blend the distinction between working in memory vs accessing the db. From one side it is very convenient, however I really feel that such performance sensitive operation should be carefully considered and that most SQL should be written by hand.

I believe there could be ORMs (or DB libraries) that makes this separation clear. Ecto gets this right although it's not an ORM: an SQL query is a composable data structure that should be explicitly passed to a separate function, belonging to the database repository, in order to be executed.

Re: How not to structure database-backed web apps: performance bugs in the wild

#205
post #141
post #128

Earlier quoted context omitted.

So you have to use sharding because your databases have limited capacity. But you can just join in the app, because the app have unlimited memory?

The app doesn’t hold the entire database at a time. The app simply does the following: 1) Get the root record(s) from id(s) 2) See what related records it needs, combine them into a list of ids, partition list by shard 3) Ask each shard for the corresponding records 4) Repeat from 2 if necessary 5) Return this whole tree / graph to the user Graph databases can do this in O(1) instead of O(log N) lookups. Relational j…

Acolyer again:

https://blog.acolyer.org/2017/07/07/do-we-need-specialized-g...

For the vast majority of use cases regular SQL databases blow graph databases out of the water. Unless you go for graph-specific algorithms like Shortest Path, and even then...

Re: How not to structure database-backed web apps: performance bugs in the wild

#206
post #146

Earlier quoted context omitted.

Sure it is show me the ORM that will support 2p commit, window functions, CTEs, JSON operators in PG (just a random set of features)

SQLAlchemy supports all of the above - in general it's really terrific about being extensible enough to support the corner cases.

It does look very nice and does support all of the above. I stand corrected (not a Python dev. :))

Re: How not to structure database-backed web apps: performance bugs in the wild

#207
post #23

I've worked on moderately busy backend platforms (~10K-20k rps handled on a ~4 e5-2650 and aiming for 5ms 95p response times). It greatly depends on what you're doing, but for the majority of systems which are read heavy (and that most certainly includes "dynamic" sites like Amazon or Wikipedia), I hold to two major beliefs: 1 - Have very long TTLs on your internal cache servers with a way to proactively purge (messa…

0 - Caching antipattern 101: key = calculate_cache_key() if not cache.has(key): data = expensive_calculation() cache.store(key, data) else: data = cache.get(key)

This is interesting because this is generally how I implement caching! What would pseudocode look like for a non-antipattern?

Re: How not to structure database-backed web apps: performance bugs in the wild

#208

This may be a little OT (OT because the points raised in the study are totally valid and mine is just a comment) but for small companies and solo developers ORM or whatever that gets the job done quickly is the way to go. Most sites and web apps never even break a 100k/day hit mark for which I believe inefficiency may not be the biggest issue. But wasting a month tryig to write native Sql queries can hurt your projec…

I build a web app for that is only used by maybe 3 persons.

Still, inefficiency is a huge issue. I need to be close to desktop-like performance for data entry, and that requiere both insert/read performance (that are executed in the same block and compromise several queries) below 1 seconds.

Request/seconds is not the metric that matter.

Is the seconds/for user(s) main activity.

Re: How not to structure database-backed web apps: performance bugs in the wild

#209
post #23

I've worked on moderately busy backend platforms (~10K-20k rps handled on a ~4 e5-2650 and aiming for 5ms 95p response times). It greatly depends on what you're doing, but for the majority of systems which are read heavy (and that most certainly includes "dynamic" sites like Amazon or Wikipedia), I hold to two major beliefs: 1 - Have very long TTLs on your internal cache servers with a way to proactively purge (messa…

0 - Caching antipattern 101: key = calculate_cache_key() if not cache.has(key): data = expensive_calculation() cache.store(key, data) else: data = cache.get(key)

Why it's an antipattern ?

Re: How not to structure database-backed web apps: performance bugs in the wild

#210
post #207

Earlier quoted context omitted.

0 - Caching antipattern 101: key = calculate_cache_key() if not cache.has(key): data = expensive_calculation() cache.store(key, data) else: data = cache.get(key)

This is interesting because this is generally how I implement caching! What would pseudocode look like for a non-antipattern?

Depends on what you are doing.

To frame it another way, what happens if 100 requests come in at the same time for that expensive value when it isn’t in the cache yet? The expensive calculation will be run 100 times at the same time.

Ideally, you’d rather refresh the cache value in the background once and never allow duplicate requests for it from the web.

If you’re running a language that makes it easier to deduplicate requests for certain data, the original approach will last longer. The CacheEx library in Elixir, for example, will only run the expensive calculation once, set the cache and then send the value back to everything that requested it while it was loading.

Post reply on HN