Live data from Hacker News

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

blog.acolyer.org

281–290 of 319 posts

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

#281

Earlier quoted context omitted.

Compile time SQL query checks... amazing! I've been trying to find a similar library for a while. Thanks for linking

jOOQ for Java does something similar. It's so good, I'm likely stuck with Java on the backend until I stop using RDBMs.

Hey Mat, thanks for spreading the love. Hope you never stop using RDBMS!

Fancy a couple of jOOQ stickers? :)

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

#282
post #56

Earlier quoted context omitted.

The ORM makes it more likely to do stuff in the application instead of the DB because many queries are hard or impossible to represent in an ORM fashion. Additionally, the ORM makes it much harder to see what is going on under the hood. For example you don't know if a value of an object is stored in the main table or lazy loaded from a related table. So you have no clue if echo "$user.name lives in $user.city" result…

This is only true if you let the ORM design the schema for you. I would hazard a guess that most people only use ORMs for queries (including mutating ones). In which case you very much do know which table a field is in.

Even if you use the ORM to design the schema for you, an ORM isn't going to just willy nilly put fields in tables by some confusing whim. Even the most opaque ORM conventions for field naming tend to have relatively easy ways to spot that MyClass.propertyName binds to table my_class and field property_name, or whatever is the case.

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

#283

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"…

Unless you're performing some magic elsewhere in the codebase, this will leak connections if an exception is thrown since you're not closing the connection in a 'finally' block. Alternatively, depending on your version of Python, you could use a 'with' context to ensure the connection is closed.

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

#284

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.

That depends a lot on the implementation of the database and the communication protocol. In our case, that made no difference

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

#285
post #82

When I was inexperienced I feared ORMs because of the negative performance impacts I've read they could have. I constantly worried about what would happen if the amount of data increased and I hit ORM induced problem that I could not resolve without major rewrite of data access layer. However, whenever I've actually hit those problems in production, I found the similar thing the authors of the article did - ORM induc…

The fact that you feared ORMs probably pushed you to use raw database, and such experience really helps you understand how ORMs work. Same is true with any abstraction – if you want to learn to use it right, first learn to make do without it.

Like so many things, it's useful to roll your own, not so you can use it, but so you can understand the complexities and trade-offs inherent in the problem space.

I've written a few ORMs before, and I can do it with fairly succinct and concise codenquickly if needed. I still reach for the full ORM from the beginning, because swapping out layer is painful, and you always want to do it sooner than later.

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

#286

Earlier quoted context omitted.

What's intimidating to me is that to make a change like that, you first need to get the code, get it and the tests running, and actually understand what the code does - and whether your fix has the equivalent result or is subtly broken. They didn't just change a single line in an application, they wrote a huge benchmark suite and dug through miles of code to find issues like this. I've no clue how much time they spen…

This is not necessarily true. You would be surprised how many absolutely trivial performance issues can be found in almost every project. Sometimes it's really just about moving computation of a constant value out of a 'for' loop.

I once sped up a program by 90% by turning `new String("foo")` into just `"foo"`. The project was still rotten though, so it ultimately didn't matter.

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

#287
post #276

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"…

What exactly is the problem you're attempting to solve with this? Bonus questions: What about maintenance or admin queries which aren't tied to a specific user_id? What about sql injection?

> What exactly is the problem you're attempting to solve with this?

Keeping all database access in one place to avoid having selects around the codebase.

> What about maintenance or admin queries which aren't tied to a specific user_id?

This is the web interface for users, all admin stuff is done elsewhere

> What about sql injection?

The selects are passed as parametised queries, so the where clause would be 'title = %s AND folder = %s'

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

#288
post #283

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"…

Unless you're performing some magic elsewhere in the codebase, this will leak connections if an exception is thrown since you're not closing the connection in a 'finally' block. Alternatively, depending on your version of Python, you could use a 'with' context to ensure the connection is closed.

Good point, thanks for that - there is a lot error handling I haven't shown but hadn't taken into account memory leaks.

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

#289
post #45

Earlier quoted context omitted.

That’s how I have seen it done in Perl, PHP, Django, etc as well. It’s not an ORM thing it is a naive programmer thing. The ORM makes it easier to do things like Class.filter(insurance_end_date But hey what do I know, I am not berating the youf of today so I don’t belong in this skit.

Class.filter(insurance_end_date Which ORM is that? It would be cool to compare some approaches to common problems.

ASP.NET MVC4 also has Linq so you can do stuff like Class.All(x => x.attribute == value).ForEach(foo => foo.method())

Django has similar functionality through Q and F expressions.

Most ORMs have this functionality of specific language constructs to allow complex queries to be expressed without writing SQL, just with slightly different semantics.

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

#290

Earlier quoted context omitted.

In postgres and mysql, at least, the database has to re-scan data every time you run a limit/offset query query. It gets progressively slower as your offset increases. The efficient way to handle it is to set a lower limit on the pkid (or other atomically increasing row) of the last record fetched, and fetch in ascending order e.g. SELECT * from my_table where id > (last_row_id_seen) ORDER BY id asc limit 20; Then yo…

What if you need to order by something other than id?

Then, generally, give the user a bunch of filtering options and limit page depth to less than ~1000 entries
Post reply on HN