Live data from Hacker News

PostgreSQL example of self-contained stored procedures

sivers.org

41–50 of 81 posts

Re: PostgreSQL example of self-contained stored procedures

#41
post #3

I'm curious if anybody has practical experience developing a reasonably trafficked website in a similar manner. I wrote apps which consisted mostly of oracle stored procedures when I first got out of college, and it was a pretty awful experience, but it was also at a place where the development knowledge was minimal. I've since been getting closer and closer to writing postgres in this way. My current app (which I ar…

Every once in a while I encounter somebody who heavily promotes that way of working. There are a couple of arguments I've heard in favor of it. Often it's a mix of performance, ultimate data integrity, and "simplicity". I just don't buy it. If performance is so critical, whatever you gain by moving everything to the database is lost by the fact that databases are harder to scale. You may be able to get by with a sing…

We write most code in backend (go), but we DEFINITELY spin up a new, real SQL database in for every test in our automated test suite. ("CREATE DATABASE" is rather cheap; use a fresh docker DB instance for each suite run, or run it permanently while developing.

Mocking away DB is both an inefficient way to work and lets the tests cover less. I will never write software without running tests against a real DB again.

Our setup totally would let you test anything in stored procedures (although we do not use them much).

Re: PostgreSQL example of self-contained stored procedures

#42

How would somebody implement this for a team with versioning, source code view, and deployment?

Why are you asking this? Just wondering why think it would not be doable.

Sql and stored procedures are just a textual source code. You can put it into version control, do code reviews, or build CICD pipelines for sql and stored procedures just like for any other language...

The only challenge is the deployment environment, as databases offer a stateful runtime engine, so you'll want to consider it's state. But state can also be managed in CICD pipelines, with scripting, sql-loader, backups, or similar.

Re: PostgreSQL example of self-contained stored procedures

#43
post #37

This kind of approach can be good for getting data into the database, especially for non-trivial updates, but is not good for getting data out. The problem is if you want things like custom group-bys, sorts, joins, windowing, etc, you end up reinventing a lot of concepts, or copy+pasting procedures with slight variations. It basically takes SQL's biggest issue, query composability, and makes it even worse than it alr…

The article basically discusses the OLTP side. You can always export from the DB to a more analysis-friendly OLAP system or just write a new set of views on the same DB that are geared for reporting.

Re: PostgreSQL example of self-contained stored procedures

#44
post #37

This kind of approach can be good for getting data into the database, especially for non-trivial updates, but is not good for getting data out. The problem is if you want things like custom group-bys, sorts, joins, windowing, etc, you end up reinventing a lot of concepts, or copy+pasting procedures with slight variations. It basically takes SQL's biggest issue, query composability, and makes it even worse than it alr…

> The problem is if you want things like custom group-bys, sorts, joins, windowing, etc, you end up reinventing a lot of concepts, or copy+pasting procedures with slight variations.

I could see how you could get that impression if you've worked on a project with poorly planned functions, but our experience has actually been the opposite of that. On the contrary, using PostgreSQL functions has allowed our company to be more consistent and declarative with our return data and has helped us eliminate repetitive SQL code. Particularly so when used in conjunction with our RESTful APIs.

Don't forget that in Postgres you can define custom composite return types (i.e., a return type that includes another return type). You can have a Postgres function that returns nested objects like this (I'm using JSON for clarity):

[ "name":"Bob" ,"age":13 ,"address":"123 Main St" ,"cars":[ { "make":"Toyota" "model":"Camry" "license":"NF98549" }, { "make":"Toyota" "model":"Corolla" "license":"NF5649" }, ] ]

The ability to enforce that kind of return type for a given function is incredibly helpful

> It basically takes SQL's biggest issue, query composability, and makes it even worse than it already is.

I think that the bigger problems are ORMs like SQL Alchemy. Where it not only introduces a new layer of abstraction but also empowers engineers to request data from the database in a completely ad hoc "unregulated" manner. In large projects without a strong lead or code review process, it can quickly turn into a spaghetti mess that seriously complicates database re-factors.

When you use functions, you have clearly defined inputs and outputs, better re-usability and much more consistent interactions between the application layer and database layer.

Re: PostgreSQL example of self-contained stored procedures

#45
One unmentioned reason this is powerful: It avoids concurrency bugs and inconsistencies introduced by write skew, in cases where the application layer (eg. Ruby/Python) makes business logic decisions based on data that has become stale.

These are hardest to detect and avoid in the case where business logic depends on no rows matching (count(*) == 0), eg. meeting room scheduling systems, because there is nothing for a transaction to lock!

Stored procedures can avoid this entirely, even under the weaker transaction isolation levels, by keeping all the logic in the DB and running it atomically. At Zipcar we took this approach to writing the web reservation system in the early 2000s. Later, it allowed us to easily add a telephone reservation system without duplicating business logic.

It worked great back then and it still works great, if you have good ways of managing the tradeoffs (version control, unit testing, etc).

Re: PostgreSQL example of self-contained stored procedures

#46
post #3

I'm curious if anybody has practical experience developing a reasonably trafficked website in a similar manner. I wrote apps which consisted mostly of oracle stored procedures when I first got out of college, and it was a pretty awful experience, but it was also at a place where the development knowledge was minimal. I've since been getting closer and closer to writing postgres in this way. My current app (which I ar…

Salesforce is built (or at least was ~7 years ago) heavily on stored procedures. Some projects, exclusively so.

Working there as a software engineer introduced me to a lot of dysfunctional things (as is true of all big tech cos) but pushing as much into the database as possible is one thing that worked beautifully and I fell in love with it. Since leaving Salesforce, I've embraced that pattern in three other projects and I've never regretted it.

It turns out that the database is the best place to work with data. It's verbose but has primitives that your application server simply can't have. And it's beautiful how querying data vs reading a variable have the same amount of cognitive overhead.

That is, the lines between what's in memory and what's coming from disk start to disappear. You just work with data, insert it when you're ready, and don't think about it again.

And the performance gains of not having to serialize data back and forth across the wire unnecessarily is huge.

I'd also be remiss if I didn't point out that if you are a fan of pub/sub patterns then triggers should be your favorite feature to use. They're just transactional pub/sub.

Re: PostgreSQL example of self-contained stored procedures

#47
post #40
post #20

I'm the author. Thanks to Ludwig for posting this here. I've been doing all of my database work like this for 5+ years now, and love it. Many more examples here: https://code.sivers.org/db-api/ It works great when you want to write simple shell scripts, too. They look as simple as this: https://github.com/sivers/store/blob/master/getdb-example.rb... People have been asking how I do unit testing. The main thing is to…

Pretty much how we built client-server applications on Oracle RDBMS 25 years ago. When your application grows, you'll have a large number of stored procedures. Oracle has packages which significantly eases the way you work with a large amount of stored procedures. Packages enable you to group related stored procedures into one object. Packages have an interface (stored procedures and custom types available outside th…

I think the more accurate translation into modern Postgresql would be to develop as an extension. This should more or less cover same functionalities.

One downside is that you need to be careful around upgrade procedures and backup because postgresql extensions weren't initially developed for persistent data. So you have to declare clearly in the extension definition which table should be backed up and kept when updating the extension.

Also if you manage your repo wisely extensions are a great way to version control. I'm currently toying around with a mostly sql app delivered as an extension and so far it's working pretty nicely. But it's a side project and it's not really ready for prime time.

Re: PostgreSQL example of self-contained stored procedures

#48
post #3

I'm curious if anybody has practical experience developing a reasonably trafficked website in a similar manner. I wrote apps which consisted mostly of oracle stored procedures when I first got out of college, and it was a pretty awful experience, but it was also at a place where the development knowledge was minimal. I've since been getting closer and closer to writing postgres in this way. My current app (which I ar…

> I'm curious if anybody has practical experience developing a reasonably trafficked website in a similar manner.

YNAB's web app (YouNeedABudget.com) is architected in a similar manner, in that a lot of logic is performed in Postgres Functions. Rails is the API layer, (which lets Rails do authentication, authorization, and a number of other things that Rails does well), but many of the performance-sensitive calculations and sync algorithm are implemented as Postgres functions.

The SQL is version controlled of course, and the functions are DROPped/Redeclared when they change, so other than being written in PL/pgSQL the development experience is similar to any other deployed web app. We chose to do it not because we expect the wrapping language to change from Rails, but because our data is very relational, our calculation and sync logic is more "easily" and accurately expressed in PL/pgSQL, and we don't have to pay a performance penalty to bring a lot of data across the wire to Ruby, run logic on it, and then ship a lot of data back to the database.

I'm not advocating this as an approach for most web apps, but it's well worked for us. It's important to get your tooling right from the get-go so that the development experience is a good one.

> I've been considering moving some things directly into the database, but I'm nervous about it because the app runs well and I like the data/logic division. Also go seems easier to learn for new developers than SQL, and it's a lot easier to deploy a new app server than it is to reconfigure a database server.

Those are really valid concerns! It's difficult to find developers who are comfortable with _advanced_ SQL, so I believe that is especially true for new developers as you point out. I think it's easier to reason about higher level languages like Go than it is to perform advanced logic in a declarative language like SQL, but at least PL/PGSQL makes that easier with constructs like loops, "if" statements, etc. Although I don't regret the decision in this case, I would have to be presented with a _very_ similar problem to architect a future web app the same way. Otherwise, I'd stick with a language/framework the team is more comfortable with.

Re: PostgreSQL example of self-contained stored procedures

#49
post #38
post #29

Earlier quoted context omitted.

Versioning: sorry I'm not sure what you mean. Same as if you had queries in an ORM and views using the results, you'd need to branch and make changes, then merge the branch together at once. I just do the same. If the database structure changes, I run the ALTER TABLE commands, when switching to that branch. Other people : I guess like anyone choosing a language, you're excluding those who don't know it. I'm assuming…

Thanks a lot for your reply! It seems the main gain for your is to move between languages and allow initial iterations/bugfixes without touching the application code. The database has more maintenance issues (like rolling txids) than the application code. I am not sure I want to add more complexity and potential issues to the database. FYI, a materialized view is a potentially long or complex query whose results are…

Unless it changed very recently be aware that you can't update sql definition of a materialized as easily as you would update a view. There is currently no "CREATE OR REPLACE" option so any dependencie build onto a materialized view can quickly become a real pain (been there). Sometime, it might be easier to stick to the old trick of a table updated by a refresher function (possibly called as a trigger).

Re: PostgreSQL example of self-contained stored procedures

#50
post #45

One unmentioned reason this is powerful: It avoids concurrency bugs and inconsistencies introduced by write skew, in cases where the application layer (eg. Ruby/Python) makes business logic decisions based on data that has become stale. These are hardest to detect and avoid in the case where business logic depends on no rows matching (count(*) == 0), eg. meeting room scheduling systems, because there is nothing for a…

> Stored procedures can avoid this entirely, even under the weaker transaction isolation levels, by keeping all the logic in the DB and running it atomically

I don't understand. Multiple SQL statements executed inside stored procedures are not run any more atomically than multiple SQL statements executed from application layer.

Post reply on HN