Just use Postgres for everything
amazingcto.com
amazingcto.com
Sure, PostgreSQL can be used for sessions, but Redis has solved this problem on virtually every single platform a long time ago. Your customer probably doesn't even know what a session is, but they will definitely learn about them when your in-house implementation inevitably encounters an edge case.
Of course, PostreSQL has wonderful search capabilities but it was never intended to be used as a search engine. Solr was created in 2004 and is still being improved and used in production every day. Do you know what will happen to your full-text search when your customer types in a mix of English and Chinese characters?
Yes, PostgreSQL addition of SKIP LOCKED was neat but AFAIK the author of that feature himself recommended using a traditional job queue unless you had a very good reason against it. RabbitMQ was designed as a message queue and when you read the documentation you realize that they had encountered virtually every single problem and figured out a way to deal with it so that you don't have to. The documentation pretty much tells you what problems you are going to have later so that you can plan for them today.
Choose boring technology (tm), follow industry best practices, and enable your team to get their work done using the right tools for the job, so they can deliver the product to the customer, and leave the office on time.
Every app has different needs and there's always some code that would be a lot simpler if it were written in-house and specifically built for the needs of the application.
Abstraction has a cost and if you take everything off the shelf you'll end up with a much higher overall level of abstraction in your codebase. Plus, your engineers aren't going to understand third-party code as well as in-house code.
I've worked at companies that wrote almost everything in-house and I've worked at companies that had a phobia of in-house code. Both had problems and I think the real solution is somewhere in the middle.
Edit: I also get a "nobody was fired for buying IBM" vibe from this.
In house has the ability to build only what is really needed and nothing more, and can adapt to your specific needs, it can also identify unique to your domain challenges and tailor solutions specific to that.
A good CTO has good intuition into when and what makes sense to invest in an in-house solution and what is best using a self-managed open source solution, and what is best using a paid managed offering, and all manners of hybrids.
I never understood this until I saw some dysfunctional enterprise companies. I've seen entire teams spend 3 months doing fuck all because they can't get a local dev environment or build pipeline to work.
If you only have Postgres, and you're weighing up whether to add a second dependency instead of installing a Postgres plugin or writing 50 lines of code, of course it's going to seem like an obvious choice ("I can get this working right now instead of spending a day or two on it! Wow!").
If you make a habit of making those decisions again and again, then your project is going to spiral out of control before you even realise it.
The "built in-house" things are the only things that actually make your company competitive and provide shareholder value. There is no value in downloading and installing commonly-available stuff from the internet.
Maintaining off the shelf software that you got from Github provides no shareholder value whatsoever, however. It is a pure cost center.
As a CTO you do not want to be the guy that runs a pure cost center department.
Sometimes, the cost (both immediately and long term) of introducing a new component into your stack will exceed the cost of building and maintaining an inferior in-house solution, and sometimes it won't. Either way, the cost of these freely downloadable services is not just their cost to download.
Sure, if your use case requires it, use Redis for sessions or RabbitMQ for queues. But you can also use a library with postgres, or even write the 40 lines of code yourself.
Each component has its own debugging requirements and tooling. Each component adds a bunch of complexity. Sometimes it's worth it, sometimes it's not. It's not as clear-cut as you're making it be. There are pros and cons to both alternatives.
My boss said he just wanted to install the best thing and be done with it forever. So we ended up spending 10x the time and wrote like 10x the code to integrate a third-party solution and we only ended up using the most basic features. Not to mention additional infrastructure.
But it doesn’t need to require 10x the time or code. That just sounds like overengineering. Database-as-queue vs. 10x ultimate solution is a false dichotomy.
What difficulties did you meet?
You are not Google and you are not Steve Jobs
Conclusion: abandon this company.. you’re probably just a cost center and nagging headache in his eyes. If you’re not valued or respected, there’s only way way up, and that’s out
You'll also need to code for two integrations (orm + whatever you're using redis with), which may be a solved problem or not, depending of your stack. And even then, still more complex than just postgres, and more error prone considering you'll either have to ignore enqueue reliability, or find a complex way around it.
Brief: yes, a specific datastore is usually better fit for the job, except that until your app requires more performance than Postgres can deliver if you already _have_ Postgres you might as well just stick to it.
Same for SQS - SQS is incredibly performant, but there is a whole lot of features it does not have which a PG-based queue system like good_job will give you out of the box. Just off the top of my head - priorities, separate queues, scheduling - and, do not forget, atomicity with your main transactional workload.
So while it is usually - when everything works great and the workload fits - not a big deal to run a specialized store, it can be more economical and simpler to just stuff everything into the DB until you outgrow that.
This is basically not true, is my point. There is no meaningful "problem" with throwing up a Redis instance in AWS, this just doesn't mesh with my experienced reality.
Failover when the node dies. Clustering for high availability?
Backups? For a cache, probably not, for a job queue broker, probably necessary.
Making sure your app deals with inserting into Redis on successful transactions and not when a transaction is rolled back.
Getting up and running can be fairly painless, staying running on all edge cases and handling partial failures is what gets you.
It’s just a nonsensical and unfair comparison. You can run a single Redis instance with normal rdb disk syncs and don’t ever update it for years on end without issue. Is that guaranteed resilient? Absolutely not, but that’s not the scenario in discussion. We’re talking about the context of a bootstrap/MVP scenario, not an enterprise setup.
I’d take a single-node redis job queue everytime over a HA citus/postgres cluster improperly acting as a queue.
Needing Transactional semantics for jobs alongside an application operation makes a lot of simpler queue/tool choices difficult.
For Redis though, the overhead is far more trivial than something like Kubernetes or Kafka, or even Elasticsearch or MongoDB
You don't need to learn your tools for doing simple stuff under normal circumstances. You need to learn them to do bespoke surgery on live data while everything is on fire and the customers are threatening to fire your company. Or better yet, so that you can avoid doing that altogether by anticipating limitations during design.
That being said, doing everything in Postgres is also going to bite you if you have moderate scale. This is really the same mistake again. Postgres looks like a big truck you can just load up and load up, until you wake up one day and there are cascading failures across all services that touch that database, because you wrote a dumb query that took a lock for 5 entire minutes while it did an HTTP request. It's robustness will lull you into thinking something is working well when it's actually barely working.
(Before you object, yes, it is a better idea not to have multiple services talk to the same database, I hear you. And no, you shouldn't ever hold a database lock while doing an HTTP request, believe me I know. These things can happen.)
I’ve been using Redis for nearly 10 years and it’s been a seamless and pleasant experience. Honestly it sounds like you’re taking your specific experiences and overgeneralizing.
I don't know the nature of the applications you've been working on those last 10 years, but it was more or less the main database for a high bandwidth, low latency service I was working on, also using Elasticache.
Problem spaces vary. If you're using it as a cache with modest load and consistency requirements, maybe you never need to understand it. But those sorts of requirements often creep & change out from under you.
So if you're saying, Elasticache did a good job of abstracting Redis, sure, I agree. If you're saying, there is no additional cognitive load to adopting a new service in your data path, because you don't even need to understand it - that puts a shiver up my spine, and makes me hear Pagerduty alerts in my head.
I don't feel designing for a guaranteed high-availability application was part of the discussion at all.
https://redis.io/docs/data-types/streams/
You can make good job queues out of this, combined with sharding or consistent hashing, for low(ish) latency applications. Each shard has a stream, they operate on data stored in Redis, and you pass them the key to this data over their stream.
But SQS is great, and a great rebuttal to the article. Totally easier to prototype a job queue that way than with pg, and you probably won't need to move off of it.
ETA: seeing your list of missing features now, that all makes a lot of sense. In my mind the biggest advantage of SQS is that it glues together all the other AWS offerings, so you can go AWS -> Lambda for an instant job queue (with concurrency limits, etc. so you don't blow your hand off - perhaps undermining the simplicity argument). But everything you're saying makes sense if your job queue needs any degree of sophistication.
- No multiple queues - No priorities - Practically no scheduling (the delay is very limited) - Creating and tearing down a queue takes a lot of time and the number of queues is subject to AWS account limits - The FIFO/LIFO semantics (remember about no priorities?) will bite you when you least expect
It does have great durability unlike Redis though and will scale to much, much larger queues in an easier way.
It's all normal stuff, by a long shot not the end of the world, but it is stuff that you need to do, and it is more stuff, and it can bite you if you come unprepared and "just clicked a few instances into existence last year".
And then there is monitoring, dealing with security issues, and upgrading.
It all multiplies.
Elsewhere people are debating whether Redis adds that much more overhead. I think they are missing the wider point. It’s about how many pieces you will have. 1 is simpler than 2. 2 is simpler than 3. 3 is simpler than 12.
Nowadays there are so many great specialized tools that do certain things really well. And for most use cases, you don’t need them. And when a time comes that you do start needed them, you add them then. And people will grumble about the idiot who implemented full text search inside of Postgres, while being completely blind about the 7 years of saved time NOT managing elasticsearch across 38 environments and quarterly upgrades.
But yeah I think this is more applied to the folks who decide to spin up a large service when they just need a messaging layer that their DB can solve for them. It has non insignificant cost.
Redis is pretty low maintenance tho in many configs (k8s, on a vm, or a service like on aws have all basically been zero maintenance for me).
Little tiny custom stop gaps are the worst in a bit system.
I'd call Postgres pretty solidly "boring technology", including for session storage and job queues. People were storing sessions in SQL databases when I got my start in 2005!
It won't address every scale and every use case, but then, that's never your project's requirement anyway.
I frequently see the term "boring technology" treated as a euphemism for "what I'm accustomed to".
Postgres can and will scale to workloads that average developers can't comprehend. I've used Postgres professionally since 2003 doing everything from run of the mill web work to high volume log aggregation systems with massive datasets. Postgres can ingest and index hundreds of thousands of records per second on pretty modest hardware by today's standards. One just needs to learn the tool.
Postgres is far more "boring" and battle tested than Redis. All the hipster tech that came out of Web 2.0 companies (including Cassandra and Redis) has hallmarks of being built by junior developers who refused to learn and then build upon state of the art and went on to reinvent many wheels quite poorly.
Forward a decade and most of it is in the dustbin of unmanageable tech while good ole RDBMS outlived them all. Mongo is about the only one I still hear about and see pop up in job ads. Might share the fate of Hadoop too when people learn that Postgres can index JSON too and even be sharded if you need to (you likely don't).
If you're starting out with a team of say 4, and that team will stay small, don't waste your time integrating complex tech.
Solr is DEFINITELY better than postgres fulltext search. However pg_search and a clever index has let me solve 95% of all our search needs in almost no time with zero maintenance. However once you want to get into search complexities, Solr quickly outperforms postgres, but you have a lot of setup and env management to do now.
So it is all about team size. My instinct is at around 10 engineers you should start thinking about which complexity is worth pulling out of postgres and into its own service based on needs.
You added RabbitMQ for that one queue use case? You suddenly need to handle some health check edge case in prod since your programmers doesn’t have experience with it. Just added redis? You now have an extra set of server and client sdks to regularly patch up.
Etc. Sure, there’s a point where it’s logical to add a new class of services, but it’s not remotely close to zero (which is how I read the comment).
Obviously, it would be impossible to maintain such an advantage in real life for any extended period of time. However, orienting your team towards that goal gives the CTO a way to quantify risks and costs associated with accomplishing their objectives. Health checks and SDKs are standardized commodities that can be implemented and maintained at a predefined market rate that's always approaching zero. Finding and fixing a bug in your proprietary code has a potentially infinite cost.
It's all trade-offs. Complexity is complexity, whether the code is yours or not.
Also, SDKs are not standardized. Third-party software still gets updated which means code maintenance for your team. And, even worse, it's software that is generally opaque because, by definition, it wasn't written in-house. I worked at a place where they had a phobia of in-house code and the end result was huge amounts of time wasted on updating libraries and debugging issues caused because we didn't really understand the code we were using.
The real answer is deeply understanding your product and finding the right mixture of in-house and third-party software that maximizes simplicity while also allowing for flexibility and growth in ways that matter for your specific product.
This is probably good enough for 99% of things out there running stuff.
For everything else you have architects which will do the right thing for you.
I see postgres like a tractor. It's the most important machine and you can attach alot of different equipment's to get alot of jobs done. Some times you notice a task need a specific machine that the tractor is not suited for, then you invest in that machine. To invest in all specialized machines from the beginning just because they can do one single job better than the tractor is not an efficient way of farming, there is a price to have all those machines too, that does not necessarily result in a better outcome.
You are absolutely right, but what if(just an example) you need transaction behavior between your queue system and your database? Good luck with 2pc or using saga and similar.
Or, as other people stated, how much will cost to maintain postgres + redis + rabbitmq vs only postgres?
In my opinion, the golden rule is: *use the most general purpose tool you know unless you hit a hard limit of the tool, in that case start moving to a specialized tool*.
Killer postgres features however: Row-level security (Fantastic when you're using something like postgrest for rapid backend development [1]), and its built in fulltext search engine is 'good enough' for use cases like when you have an enormous users table and need to index something simple enough, like email addresses for quick login.
https://www.postgresql.org/docs/current/pgtrgm.html#id-1.11....
The positive response of the address verification will tell you the address is deliverable and the user has access to it. Later if someone tries to register a capitalized form of the address it'll get rejected because of that account ID collision. Then the user can be pushed to a password recovery path where they'll need access to the e-mail/MFA to get control of the account.
Amusingly, the context of this thread was in using case-insensitive search for email fields, but if emails are truly case sensitive, this is all moot, because you can only do direct comparisons.
If you run into a case where their e-mail server enforces case sensitivity they have bigger problems to deal with. E-mail has long been a system that requires loose adherence to the specs.
IIRC, as per spec/rfc, e-mail addresses ARE case sensitive.
However the de-facto standard is to ignore such thing and deliver emails for Bob@example.com, bob@example.com and BoB@example.com all to the same mailbox.
It sounds like you might be unfamiliar with the common trick to index long text fields in Postgres: you just make an index of hashed values, and use that for lookup as well to ensure index gets hit.
In case you are familiar with it, maybe it helps someone else who stumbles upon this. :)
the database should be able handle case insensitive indexed lookups directly.
The other thing is that if you're inserting your emails in without running some ToLower() function on them first in the validation, you're probably making a bit of a mistake. There's some other discussion in the thread about this.
When you get over a million users...
I've yet to try the index of hashed values trick though, when I revisit this problem in the future I'll make sure to take note of this!
The reason is that indexing on a hash produces a much smaller index so more of it can fit in memory. By minimizing disk seeks, you can speed up query time even if the Big O is the same. In both cases it should be O(log n) since SQLite uses btree indices.
Also note that you can have multiple indexes in Postgres (well, any RDBMS).
For emails in particular, you can even do partial indexes (eg. imagine filtering emails on @gmail.com to hit a separate index that's only half of your full table index). This requires some care with queries, but a wonderful feature regardless.
I’m pretty sure that still necessitates a hit to the db to prevent false hashes, but that’s going to be the case with your approach, too.
If you need any normalization like lowercasing, you need to do that yourself.
Next, you can't do collation/ordering using the index (and greater/less than comparisons), unless the hash function maintains ordering properties.
By having an option of an expression index customised to your data, you can use it to get the fastest query performance.
Modern, production-grade, web-scale machines are able to run more than one process, these days.
Plus, not everything is a public-facing website. It's amazing how little overhead it takes.
exactly. single-node availability is often "good enough." when using sqlite for a simple service, I've cranked things out from first-line-of-code to production in 30 minutes. When the app only gets a hit every minute or so and only from internal traffic, no need for the overhead of a highly available service.
You'll have to shut the app down to back it up. That problem gets worse if you want some kind of a cold standby, since backups should be frequent.
You could replicate it, but that requires running a separate daemon and starts to beg the "why not just have Postgres be the daemon?" question.
Sysadmin tasks start to get very difficult when someone starts with SQLite and expects to get Postgres-like features out of it. I'd rather run Postgres than try to replicate or continually back up SQLite.
This is not the case:
1) SQLite recommends using the official backup API, rather than copying files on disk. The backup API can be used while the app is running.
2) Litestream is the hot new tool on the block. It streams incremental DB changes to a backup stored on S3, for up-to-date point-in-time recovery.
Do that until scale forces you to start specialisation.
It's surprising how far you can go with database-as-MQ. Even database-as-IPC can work for smaller systems.
A lot of startups are convinced they'll need Google scale from day one. Then, of course, the overwhelming majority fail in the first year.
Get a big, reliable, and cheap vhost/server somewhere, use as many "can't really go all that wrong" components like postgres, minio, etc and dockerize everything. If you want to get "fancy" use ZFS and setup some snapshots and backup. Most solutions don't even need 100% uptime. Communicate maintenance windows to customers and you'll be fine with a total of an hour of downtime (or whatever) in the first year. Most big, really complex, early over-engineered and unnecessarily "optimized" solutions have enough footguns you'll probably end up with more unscheduled downtime in the first year anyway.
In the rare event the startup really succeeds and customer demand, load, uptime requirements, etc demand it you can throw revenue/funding/etc at a K8s control plane on your favorite hosting provider, use a managed postgres/db/whatever, and S3 compatible object store, etc. Or, if things get really big skip all of that and hire in house talent to manage a couple of racks of leased hardware (same opex as cloud but almost always SUBSTANTIALLY cheaper) in geo redundant/distributed colocation facilities.
I've launched multiple startups with this strategy and it's gone very well. My current startups all run from the same big (but 10yr old) hardware that has loooooong since paid for itself even with lots of GPU, storage, etc upgrades over the years. People can be kind of scared of hardware but I've never had downtime or data loss caused by a hardware failure in almost 20 years of this approach.
People are always amazed when I do things with ML, TBs of data, lots of bandwidth, etc and I tell them my total hosting costs are $150/mo.
I'd love to hear more about the setup. I suspect something as simple as disk failure would cause outage, although I suppose you can detect a soon to fail disk via SMART and resolve that with scheduled maintenance/downtime. But what about power supply failure? Do you keep redundant backup parts on hand?
There's definitely something nice about having a hardware error on a cloud VM result in that VM cycling out to new hardware automatically. In contrast, something as simple as buying a new off the shelf PSU feels like a ~1 hour downtime event (longer of you don't have purchase card authority, it's night time, you need to order online, etc.).
The system I referenced currently has x8 4TB NVMe drives on ZFS raidz2 and x8 16TB rust drives also on raidz2. I use sanoid to snapshot like crazy (down to 15 minutes) and then syncoid to push snapshots and then some to the spinners ZFS array. Plus zfs send remote to offsite.
Modern switching power supplies are incredibly reliable. Then do proper “half load” dual power supplies from dual conditioned power feeds with UPS and generator. Losing power to a machine almost implies an extinction level event or complete incompetence on the part of a data center operator.
I think you should keep complexity down by only using postgres, but just use it as a sequel database until you scale enough that it's a problem.
90% of the time it's never going to scale that far.
When it does, use the tools people have made to do those jobs correctly. Do not hack you way into half-made tools put into a database that isn't specialized in that job.
It seems like you're making your life more simple by having only one system, but you're having that one system to so many things at the same time that it's going to be an absolute kludge in the long term.
Does anyone have experience trying something like this? If you did, is this how it turned out or did it actually work all right?
> A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system.
A SQL database, maybe supplemented by a cache, can carry most projects as far as they'll ever go. And if you outgrow it, replace it with something that meets your new needs.
Any given project's "right tool for the job" can change over the project's lifetime, and optimizing for problems you don't have is quite harmful.
With Postgres it seems to be inefficient at storing rows with large amounts of columns, and "upserting" data (using UPDATEs or INSERT ON CONFLICT) results in huge amounts of disk writes, I think because Postgres writes the entire row even if a single column has been updated.
I could store some of the regularly updated columns in a separate table, but I'd thought I'd ask, is there a database out there that is more suited for this type of workload? Because I feel like "just use postgres for everything" is not the answer here.
You might benchmark it against a couple other database flavors, I suspect most dbs would have issues, although maybe not the same issues as postgres
You might want to have a look at HOT [0] tuples if you haven't already.
[0] - https://www.cybertec-postgresql.com/en/hot-updates-in-postgr...
Eventually maybe PostgreSQL will get a second storage engine that uses an undo log instead, like the somewhat stalled zheap effort for instance.
That said the numbers of rows you are talking about are easy peasy for PostgreSQL.
This is very different from columnar/column-oriented databases which are still (usually) relational and just store data by columns instead of by rows for OLAP (large-scale analytics) usage.
Not the same-same as a "column-store" like Druid or Clickhouse or Pinot.
[Disclosure: I work at ScyllaDB.]
[0] - https://www.timescale.com/blog/building-columnar-compression... [1] - https://www.timescale.com/blog/timescaledb-2-3-improving-col...
Postgres and other OLTP databases are a better fit.
But 40 columns should be a cakewalk for any RDBMS to handle. And I assume you're not storing KB's of data in the different columns? Because if your strings are large enough to be allocated as references to separate blob storage, rather than in the row, that could be a performance problem.
Bulk operations with millions of rows can also sometimes take way longer if you're doing them as a transaction that can be rolled back. If it's acceptable to disable that, that could be a huge improvement.
You can also sometimes find massive speedups in using bulk SQL statements (upserting 1000 rows per query, rather than 1 row per query) or CSV file import rather than SQL.
And obviously make sure you're using indexes wherever appropriate.
Because generally speaking, upserting ~10 million reasonably-sized records is the kind of thing that should only take a few minutes on an SSD. You're not going to do it in seconds, but it shouldn't be taking an hour or anything either.
This seems like a bizarre rationale to make a database choice.
Your database and SSD are functioning totally normally, as designed.
This is because of the consistency model postgres uses: Roughly, the old row remains in existence as long as transactions started before the UPDATE are still running, and the new row exists alongside it for that duration. Transactions started before the UPDATE can't see the new row, and transactions started after the update can't see the old row, because the transaction id (txid) is added to the query and is compared to hidden columns on each table/row (xmin and xmax).
This is one of the things VACUUM cleans up, actually deleting those old rows once all the old transactions have ended.
[1] https://www.cockroachlabs.com/docs/stable/column-families.ht...
Consider remodelling the data model and going up to 3NF [0], or to a higher normal form if that is more appropriate for your use case. Throwing different relational database engine types at the problem might help with bandaiding the problem for a while, but one day it will come back and bite you again. Wikipedia has a good starting point [1] for the 1NF ↝ 2NF ↝ 3NF ↝ … design steps. Depending on the nature of your data, you might have to consider the Boyce–Codd normal form (3NF++ so to speak). Maybe not.
Once you have remodelled the data model, selects and upserts will be very efficient, quick, and they won't result in the constant index update/rebuild for the entire single main table. You will have to rewrite your queries, but if the data model is normalised, joins will be straightforward, fun to write and will only update what has actually has changed in each, separate table.
If the data model normalisation is absolutely impossible (e.g. you don't own the data model but rather your customer does), consider replacing upserts with the MERGE statement.
[0] https://en.wikipedia.org/wiki/Third_normal_form
[1] https://en.wikipedia.org/wiki/Database_normalization#Example...
First is that MERGE is a much more versatile operator (not a compliment if you need a specific narrow functionality like upsert) whereas INSERT / ON CONFLICT was specifically added to make thread safe upsert simple. How would you do thread safe MERGE in Postgres, off the top of your head? It's something that ON CONFLICT solves for you.
Secondly, MERGE is only available starting with Postgres 15, which has just been released.
So no, don't replace upserts with MERGE unless there's a gery specific reason.
MERGE does have a number of its own peculiarities that are database engine specific and the MERGE behaviour is not portable across RDBMS's.
For instance, Oracle simply does not care about the transaction context width in which a MERGE is used, and the transaction will either succeed or fail no matter how long the transaction takes. Whereas MS SQL Server is very, very touchy and will kill off the transaction if, say, an index update caused by a MERGE is taking too long (from the SQL Server perspective). So it is better to minimise the transaction context containing a MERGE for the MS SQL Server and carefully assess the index update performance. I would expect Postgres to have the behaviour closer to that of Oracle, but I have not looked into it.
More generally speaking (and if we ignore MERGE implementation specific details for a moment), remember that the UPSERT pattern has come about as a workaround for the missing MERGE functionality which was added into the specification very late, and vendors were slow to implement it. MERGE covers a few very useful use cases, especially for complex UPSERT scenarios, but it does not obviate UPSERT's in simple scenarios. Both are useful.
Regarding SQL Server - it's not true that SQL Server will kill transactions based on some timeout (unless there's a deadlock detected). I worked with SQL Server extensively, including using MERGE for upserts (btw, you have to use serializable isolation to make MERGE thread-safe, so it's not so easy to get rid of transactions altogether). SQL Server doesn't kill transactions, except when there's a deadlock. FWIW, in SQL Server community there's a well-known notion of MERGE being buggy (even though it's been supported for like 15 years!) and hard to reason about (especially when there's triggers on the involved columns), see for example: https://www.mssqltips.com/sqlservertip/3074/use-caution-with...
I agree with you that Postgres' MERGE is most likely modeled after Oracle, but haven't looked into it either.
I, for one, find the MERGE syntax to be easier to read, more flexible and versatile and, most importantly, standardised. It is, effectively, UPSERT++ if you like, as it also allows one to delete rows from a table if there is a condition match. Compare
MERGE
target
USING
source
ON
target.col1 = source.col1 AND
target.col2 = source.col2 AND
…
WHEN MATCHED THEN
UPDATE SET -- or DELETE
target.col3 = source.col3,
…
WHEN NOT MATCHED BY TARGET THEN
INSERT (…) VALUES (…)
with INSERT INTO target (…)
VALUES
(source.col1),
(…)
ON CONFLICT (target.col1)
DO UPDATE
SET target.col3 = source.col3;
Personally, I prefer the MERGE option, but your mileage may vary.I also deem MERGE, as a technical term, to be more concise and much closer semantically to the intent it describes as opposed to INSERT ON CONFLICT. Rows are routinely updated in a database (it is its job after all), and a row update operation is not inherently a conflicting update. Conflicts (semantically) confer exceptional situations that have to be dealt with, well, in exceptional ways. But I digress as it is more of a lingustic subject.
> […] it was added rather as a proper solution, but for a more limited use case which is popular in OLTP workloads: simple thread-safe upsert.
As far as SQL (the language and the standard) is concerned, SQL is unaware of threads or the thread safety; SQL is concerned with transactions and with the transactional integrity. The database provides and ensures the ACID behaviour and guarantees, and it may or may not even use threads to accomplish it (as a matter of the fact, we know that most do but it is an implementation detail).
And MERGE is perfectly suitable for a variety of processing scenarios OLTP including. In one of my past projects from a few years back, replacing a series of handrolled UPSERT's with a single carefully written MERGE yielded a 1500% performance increase for real time freight item scan events flowing into the main table (granted, the inability to improve the shoddily designed schema was a hard constraint therefore a certain level of creativity was necessary). It was for a 10+ million processing events per hour scale, which is not huge but substantial.
> MERGE definitely has its place in complex ETL-style workloads
As an aside note, ETL workloads are not expected to modify a database in place which would otherwise make them one of the most vile and reviled integration anti-patterns. The (E) and (L) steps imply a distinct source and a distinct target, and the (T)ransform step takes place outside the DB, either in the integration layer or is done by a specialised ETL tool. That aside, I fail to see what is so ETL specific about MERGE; if there is a complex UPSERT scenario, a MERGE could be a good candidate (or not), the onus is on the engineer to carry out the analysis and make the right choice.
> Regarding SQL Server - it's not true that SQL Server will kill transactions based on some timeout (unless there's a deadlock detected).
It is true (or it was in SQL Server 2012). In the project I have mentioned earlier, I inherited a design and an implementation that were nothing short of a unmitigated dumpster fire, which also included «let's add another few random compound indices because what could possibly go wrong?». That had to be scrapped and re-engineered from scratch, as it turned out that SQL Server had a penchant for allotting a specific time window for a transaction to complete (irrespective of whether the transaction was serialised or not), and if an index update was taking longer than the time window allotted to the transaction, the DB engine would kill the transaction off. Such a peculiar behaviour was so unexpected that it caused multiple catastrophic cascading failures during the first production deployment attempt, and the deployment had to be aborted and rolled back. I had to enlist a DBA to work on the post-mortem and the subsequent redesign to understand the root cause. It turned out to be the documented SQL Server transaction engine behaviour. A painstakingly difficult, lengthy and time consuming low level index performance analysis and a subsequent meticulous complete index redesign solved the problem in the end.
When I say thread-safety, I just mean the general notion that running a certain SQL statement or procedure concurrently from multiple processes/threads/users/connections will result in correct execution without race conditions.
SQL is definitely concerned with something that is very close to the notion of thread-safety, namely transaction isolation (I in ACID). It's the property that controls concurrent execution of queries in the database, and it deals with what is called "phenomena" in SQL literature. Phenomena is essentially a set of specific types of race conditions which occur under different isolation levels.
And because default isolation level in most popular DBs is "Read Committed", - that is, a very relaxed isolation level allowing a lot of race conditions, - some of pretty basic operations such as upsert/merge are not thread-safe (or, if you dislike this term, you may say "have race conditions", or "do not avoid certain phenomena").
> It turned out to be the documented SQL Server transaction engine behaviour.
Would appreciate the link - it's either something that I haven't seen, or we're just using different terms for something deadlock-related.
>(T)ransform step takes place outside the DB In a perfect world probably yes, but in reality there's plenty of cases where you have some staging tables that are then merged with production tables - that's where MERGE is a good candidate, as it can handle all three of insert, update and delete.
Cheers!
PostgreSQL Extensions: http, pg_cron, timescaledb
To help with development, I am using https://www.npmjs.com/package/sql-watch (written by myself) to do continuous development and testing (TDD/BDD).
The stack "doesn't scale" but the turn around time for development is crazy.
Because it's always that guy making manual DDL updates that screws everything up, isn't it Mr. Hosick. ;)
It's why AWS Redshift exists: Postgres with column-oriented storage.
> Use Postgres as a message queue with SKIP LOCKED instead of Kafka (if you only need a message queue).
I wouldn't describe Kafka as a message queue. It's more of a distributed commit log.When people talk messaging they normally mean something lower latency, potentially out of order and where the success of each message is likely independent of other messages. For these use-cases Kafka is not well suited. For these cases you will want something like Pulsar, SQS, PubSub, etc.
1. https://kafka.apache.org/documentation/#uses_messaging
I use (and think of) Kafka as a message bus, with distributed commit logging being the mechanism for how it accomplishes that task.
Because it's really a log and it's partitioned it has a whole host of issues that make it a very poor messaging system like hot partions, head of line blocking etc.
If what you want is a proper messaging system where the underlying model is still a distributed log then you should look at Apache Pulsar. It can provide the same semantics as Kafka but can properly implement job queues and messaging semantics ala SQS, Google PubSub, et al.
For God's sake, please don't
- they live in source control
- they are covered by automated tests
- they are applied using some form of automatic database migration system (not by someone manually executing SQL against a database somewhere)
If you don't have the discipline to do these things then they are likely best avoided.
I'd go further and say you should avoid databases and maybe even persistence entirely if you don't have the discipline to do the above. Sprocs will be the least of your problems otherwise.
so that probably excludes 95% of legacy codebases out there from the 90s,00s
For "you" in my comment, please read "one" instead:
> I think stored procedures can be perfectly safe provides one follows these rules:
> ...
> If one doesn't have the discipline to do these things then they are likely best avoided.
This is the classic "carpenter blames his tools for crappy results" argument. Implementation isn't easy.
Now I do think there is a benefit in stored procedures and triggers (E.g. for audits) if they don't contain too much logic or complexity.
I think this is the catch. Most folks who are arguing against SP have been burned by huge complex stored procedures with nested dependencies with deeply intertwined business logic and rules. I completely agree that you shouldn't use a SP in that way. But to help perform maintenance, or to audit, or perform data correction all make sense when kept small and simple.
As a side note, I did not realize $diety was concerned about DDL/DML, so thanks for pointing it out. I never really thought about it.
Stored procedures are much faster than writing logic in some remote server (just by virtue of getting rid of all the round trips), require far less code (no DAOs, entities and all that crap which simply serves to duplicate existing definitions), and have built-in strong consistency checking primitives - which can even be safely delayed until the end of the transaction.
And what people do is, they throw all these advantages away because they can’t be bothered working out how to integrate the stored procedure code ergonomically into their workflow.
I mean - I even use an IDE (JetBrains) to write pl/pgsql. It’s just another file in my repo. Get to this point and stored procedures are a game changer.
We made this tool to get the best of both worlds:
So… why exactly would you exclude compute running close to the storage?
You can use that minimal latency.
Of course, people can create an uncontrolled mess of spaghetti code out of sps/funcs… like they can with any kind of code.
Very hard to overstate the benefit of having all data in one DB that your developers can trivially run, mutate, and step-debug in your application.
But: I’ve also had to deal with “SPAs” that evolved a few dozen independent routes a massive backend all coupled to the database with no ORM/class layer. This was not so nice so refactor when it came time to scale
1. Teach people transaction isolation levels.
2. Have some standard written down rules about what SQL type to use in which situation and what approaches to use for table structure.
3. don't overuse triggers or stored plSQL procedures or similar they are hard to test and debug
The two main problems I have seen with SQL databases is:
1. People not understanding transaction isolation, most inconsistency bugs or strange behaviour no seem to be able to explain I have seen where due to this. As far as I can tell this is a HUGE problem, even through SQL databases are used much less and at least theoretically it's normally through in any bachelor level course about SQL.
2. people wasting time on discussion about types and table structure. For example weather this or that int type should be used,for a lot of use-cases it's just fine to use bigint (64bit) initially for everything. With 32bit there is often some edge case in which it isn't enough. For example if you do 5_000_000 increments per second on a 31bit (i32>=0) counter it overflows in ~7 minutes but if you the same with a 63bit counter it takes ~58494 years. Sure you sometimes might have to go back and optimize storage and there are cases with clearly constrained numbers, but it's the best default for not clearly bound integer numbers. Similar "initial start with" default approaches can be written down for most situations in just a single DIN-A4 page of paper or so.
When the company rapidly grows you get mysterious bugs - in the worst case customers seing other customers data because of the wrong transaction levels.
through that:
> in the worst case customers seing other customers data because of the wrong transaction levels.
should not happen even with wrong transaction levels, that indicates some additional serious design problems IMHO, likely related to premature optimizations
From my understanding getting this kind of error would involve something going wrong at a pretty low level? I am not sure how a developer could cause this at the ORM level.
You can't effectively use an ORM without detailed knowledge of the generated output unfortunately. It does not add locks for you, so it's probably just wrong in prod when horizontally scaled and requests span multiple statements.
I don’t recall ever having a transaction isolation issue with my busy stored procedures - though I often use SELECT .. FOR UPDATE which is maybe cheating because an ORM can’t do that efficiently - but I’ve certainly seen them in ORMs.
Really!!!
Mandate automated DB unit testing [0] from day 1, just like you would for the rest of your code base.
[0] - https://pgtap.org/
I just don’t find this to be true at all, quite the opposite in fact.
It’s true that (AFAIK) there isn’t a stepping debugger for pl/pgsql, although I would love to hear that I’m wrong.
But it’s trivial to debug procedures using RAISE NOTICE commands, and you can run your tests non destructively (in a transaction that you abort) which makes setting up the state for debugging much easier.
I also personally find that I write fewer bugs in plpgsql simply because there is less abstraction (no DAOs, ORMs, caches etc) and I’m working much more closely with the actual data structures I care about. And the referential integrity checks tend to catch those I do write, early.
You'd be wrong there: https://www.pgadmin.org/docs/pgadmin4/development/debugger.h...
Well, same people won't have much success with NoSQL either, as they inevitably will skip reading on how this particular NoSQL DB handles it.
> Use Postgres for caching instead of Redis with UNLOGGED tables and TEXT as a JSON data type.
> Use Postgres as a message queue with SKIP LOCKED instead of Kafka (if you only need a message queue).
I'm sure if we try hard enough we could find some sort of meaning in these points, but then the content is coming from the reader not the author.
Also from the author's mastodon:
> Using ChatGPT As a Co-Founder
You could’ve pointed out the lackluster json support or lack of generalized indexes, but this? Come on.
Postgres wins on (lots of) datatypes, indexes types and plugin ecosystem, eg Postgis is great. Per process connection is meh, but can be mitigated. It loses a lot on all features based on operating system libraries, different OSes give different results, not great, not at all. Also if you are using postgres at enterprise level, better have some kind of support, which may cost a lot.
So far I had less issues with MSSQL than with Postgres. YMMV, ofc.
I am using both PostgreSQL and MSSQL in the production since 2003 and they are simply not in the same league. Especially tooling around MSSQL is miles ahead.
Saving ops resources by managing fewer services when getting started is a good idea, until scale necessitates dedicated technologies for certain components.
I agree that hedging against having to move from a FOSS database is pure waste.
Obviously, if you anticipate to have very high traffic and applicable usage patterns, do use that advice, but if your apps anticipated usage pattern is in fact not "very high rps per buck made", then I recommend the opposite.
I've went the whole way from very-well-abstracted-away services/repositories to an almost complete lack of abstraction.
Direct SQL or ORM in your functions, operating on many models at once, with a real postgres database available to all your unit tests, treating the SQL code as part of your application logic. Transaction per test so that unit tests are fast to run.
I've been very happy since. SQL is very powerful if you use it as SQL and not as a glorified KV store.
This is the part where I don't agree, as SQL is very expressive, and needlessly loading data to your app is also not performant (making the transition to something else required sooner).
I think writing parts of your business logic in SQL that make sense to be written in SQL is just fine.
If you only use it to load and write entities, then that is basically a glorified KV store.
Concretely speaking most startups should be using RDS or whatever hosted Postgres their cloud provider offers, not running their own.
I think it’s sensible to use what one knows best, but for default advice to others I think ”simplest viable option” is generally better.
So does a distributed database. A primary/replica has an ops burden too, and measuring that complexity is subjective.
With primary/replica you still take downtime during failover, you need to make sure you're testing the failover, upgrades aren't zero downtime in a lot of cases, etc.
You're essentially treating your database as a pet and a clustered option is much more cattle vs pet. This saves you time to develop other features (including working on reliability -- the most important feature)
I don't agree to this post.
One should use old, proven and stable solutions. If you need message queue, you need rabbit MQ. If you need cache, you need Redis. Don't implement non-standard non-conventional solutions if you can avoid it. Find out what's industrial standard and use it. Avoid dying solutions, avoid new solutions.
But also remember that you should have something unique. You don't want your business to be easily repeated by someone, because you will compete to margin zero. It's not necessarily tech thing. But it might be tech thing. You don't build cloudflare using off-the-shelf nginx.
https://about.gitlab.com/blog/2022/04/29/two-sizes-fit-most-...
Who does this? "give it to the API"... ? Still sounds like there's "server side code" there if there's an "API" involved. Surely they're not meaning let front-end code hit the database directly?
I've obviously not used it and to be honest, I probably wouldn't. But IMO Postgres is a radically different type of framework. People just think of it as a SQL database, it can be much more though. Tooling is just a bit lacking.
Edit: Heh, Retool is sponsoring them. "PostgREST for the backend, Retool for the frontend". Cool idea. I might reconsider...
It doesn't matter anyway. Serving HTTP is not a problem particularly well solved by Postgres, and needing an extension instead of whatever hyper-optimized http server you usually use is increasing complexity, not decreasing it.
I find the thing quicker than asking google, pretty often.
I really want a CLI interface to have an "ask the oracle" kind of feature.
PostgREST is page two of "postgres http server" for me.
In this case they mean "let Postgres generate the JSON for API responses" instead of having the API interface do it. The "Generating JSON in Postgres" section in the linked article shows how this is done.
yes, if you can create some JSON without having to transform in an intermediate code layer, that's handy/useful. I often don't run in to too many scenarios where things are trivial enough to allow for that - there's usually some app-level logic that comes in to play with respect to visibility/permissions/etc.
Why not? You can make Postgres speak SQL or GraphQl it removes an additional network indirection and there are very clear ways about how Posgres does scale well and where it doesn't scale well. (EDIT: Or put a dump translation layer/service in-between they exist to and are cheap to run and manage.)
You can write your core logic in JS, Rust etc. and plug it into your database as virtual tables or shared procedures (it's surprisingly simple).
Any DDOS protection and similar is anyway in front of whatever you write as some form of proxyish thing.
And just needing to scale Postgres instead of a bunch of different systems is so much more simple. Depending on how you run it and what input characteristics you have it might even be more efficient and cheaper to scale that way (or it can be noticeable more expensive) and it's most times cheaper to manage.
Not that bad actually, but it varies by use case.
Reasons aurora can be cheaper than you think:
1) Autoscaling. Adding a new reader takes 15 minutes. Instead of provisioning for peak traffic and paying for it 24/7, run an extra reader for a few hours each day. 2) Metered IO. Aurora IO can handle 400k+ iops when needed. Or it can hum along at 100 iops. You pay only for what you use, you don't have to provision for peak load 24/7.
Switching to Aurora saved us money over vanilla RDS. Self hosting postgres may be cheaper, but not by as much as would first appear.
My rule of thumb is about $1,000 per month for a reasonably large DB in RDS.
We also had a few issues where aurora was giving us *incorrect query results* and aws support were not helpful. Most of the issues were denied, even with simple reproductions, and then got fixed years later (~2-3) after we had given up. We still use it today but stay away from cutting edge features, unusual query patterns, and high volume use cases.
Having said all that, SQL still seems like the correct choice for getting an MVP out quickly.
The main thing I like about MySQL-based solutions is the availability of Galera multi-master software, which for simple configurations is very easy to get going. Drop a fairly cookie-cutter config on each host, and you're done:
# Galera Provider Configuration
wsrep_on=ON
wsrep_provider=/usr/lib64/galera-4/libgalera_smm.so
# Galera Cluster Configuration
wsrep_cluster_name="test_cluster"
wsrep_cluster_address="gcomm://First_Node_IP,Second_Node_IP,Third_Node_IP"
# Galera Synchronization Configuration
wsrep_sst_method=rsync
# Galera Node Configuration
wsrep_node_address="This_Node_IP"
wsrep_node_name="This_Node_Name"
Combine with keepalived on each node which does a health check, and if one goes down/bad, the vIP is moved over to another host in the cluster with minimal fuss.Zalando's operator use Patroni under the hood, to create a cluster over streaming replication. It also has Spilo, which orchestrates pg_basebackup or WAL-E for point-in-time backup. https://github.com/zalando/postgres-operator#postgresql-feat...
CrunchyData operator seems to have built their own streaming replication system coordinated by Raft. https://access.crunchydata.com/documentation/postgres-operat...
Both are fantastically featureful well-integrated operators that are super well maintained. Both are very recommendable.
The author forgot to mention LISTEN/NOTIFY feature in PG. Perfect for low volume pubsub.
https://docs.dapr.io/developing-applications/building-blocks...
Currently integrating Procrastinate (https://procrastinate.readthedocs.io/en/stable/) to use Postgres as a job queue in a Django API backend.
Dropping dependencies is very nice especially when dealing with an MVP / small apps.
But also, Celery has an awful DX. I think the question is more, why do I need celery when Postgres can do the job itself?
- After just the first couple of months we had to replace full text search with Elastic because it just wasn't up to the task fully. - We did introduce redis for cashing which was maybe 2 hours of setup overhead (using Rails, deploying on Render for $5/month).
YMMV.
If instead we're talking about a fast growing startup, a single solution like Postgres (btw, I adore Postgres, to be clear), it's going to become an issue.
You can dramatically improve your performance if you allow the use of Redis, as a start. And your title could have been "Just use Postgres + Redis for everything", and it would have been half as bad, or quite good.
I think it's ok to try to keep things simple, but limiting yourself to JUST Postgres is not going to work.
What is your definition of success? I’ve seen Postgres scale to pretty large loads. I’ve also seen plenty of complex stacks where the scale never warranted the complexity.
That said, Redis + Postgres is a pretty simple stack, so I’m not complaining about your suggestion. I’m more curious about specifics.
Keep complexity down to min to make it functionally work. Loadtest and fix the bottlenecks. add complexity when you know the gain.
...geospatial types were just a nice addition later. ;)
mySQL worked great for my lil piece of company business involving a few million records. But then I volunteered to do some analysis with files an order or two of magnitude larger (10 mlllion-100 million). mySQL started coughing up with memory errors. Uh oh. Embarrassing.
I should mention that I'm doing all this on a Windows desktop, as that's what is installed and locked down on the machines they give me.
Enter postGRES. No matter what I throw at it, it gets to end of the queries without errors—in Windows, no less. With an nVME and 32 GB of ram, I can run a complex report on 70M records in seconds. Loading and indexing takes about 15 min.
mySQL was very very good to me for a long time, but for the really big stuff I'm sticking with PostGRES.
You want data analytics, financial reports, the ability to query your data in new ways you didn't think of when you first built the product? NoSQL comes at a great cost.
When you hit a million users, the entirely of your codebase will be on-fire with scalability issues anyway so it won't make any difference?
Isolation?
Please do elaborate.
But saying this would not make for a blog post. I can’t help but categorize this post as juvenile at best. Generalizations like “use XXX for everything’ should be avoided at all costs. No software product serves well to such sweeping generalizations. I’m surprised that it’s coming from a CTO. They should know better if they’re worth their salt .
No matter the scale or how big it is.
If you are talking of specialized solutions, that means you know exactly where an existing, well known solution is failing you and why.
Eg. if you drop all foreign key references and constraints in Postgres, you might get similar write performance to other databases which can't make those guarantees when you do need them.
That's Postgres.
"Just use the right tool for the job at hand."
Yes, but I have heard exact that phrase for decades to rationalize tech decisions for tools that weren't needed.
I know you're different, but think of all the people who have the same problems.
If you need complexity b/c you're Netflix or Uber, go ahead. If you're the 95% others who don't need that complexity, then don't do it.
"I can’t help but categorize this post as juvenile at best."
Thanks, I guess this is the nicest thing to say to someone 50+!
Your username is also juvenile, well done you!
Fwiw, my experience has also led me to be on the side of "just use Postgres" unless required otherwise. I've seen enough people glue whatever crazy technologies together where a relational database would've been enough.
It was Codemonkeyism before (𝅘𝅥𝅮 "Code Monkey think maybe manager want to write god d** login page himself") but I thought I had grown to be king of the asylum.
Postgres is an all-purpose database that has it all: relational databases were created to model real world problems, and while they might not perform best for all the usecases, they can usually model them just fine while protecting from many programming errors.
And then Postgres has features on top: partial indexes, object DB features, replication etc.
I have two competing values in this space: someone else has solved problem x better than you will, and you will live to regret every dependency you introduce.
Using pg instead of kafka works great until you have to ingest a few gigs of data a second.
Using pg instead of mq works until you have a few thousand messages a second.
Etc etc.
What the heck?! No!
If you need something like Redis or memcache, use that for anything that is heavy on writes and deletes.
Annotate whether this is a hot cache or cold storage, and be able to flip out the infrastructure.
ETL frameworks support this. Probably should be more widely used (web devs, etc).
Serious question, though: when the data span several instances, how well does PostGreSQL do compared with Elasticsearch?
We had great ideas about scheduling and caching and task priorities...
...and then we asked the customer what they wanted, and they wanted none of it.
So we built none of it, and produced a solution that just did the stupidest thing, and did it without any edge cases, without ever crashing, reliably, as a cronjob, once on Sunday night.
Some people would be disappointed that they couldn't put this on their CV because it didn't involve FancyTech #413, but damn it, I am still proud of that stupid thing.
I asked him what he’d done to work around the technical difficulties, and it turned out that he’d set up a Wordpress site with a phone number of the guy running the scheme, and a Google sheet to manage contact details.
A better definition of MVP I’m yet to see.
I still have nightmares of post-redirect-get complexity and giant balls of mud in session scope to support the back button.
Then rather than saying just use Postgres you say just use X.
I like postgres but I've only used it in production once. My experiences were fine. We used alembic for database migrations. It was a Python Flask app.
We also used it as a message queue and stored JSON form data.
There's still hand crafted code for using Postgres as a message queue.
A bit like Linux distributions which are aggregations of desktop or server software collections of well integrated software.
I tried to build a stack that could be spun up with all goodies included.
But I would not want to inherit something hand stuck together.
But at the same time, the thought of setting up Kubernetes for everything from scratch is also a lot of work.
If you need to use cloud, that's also a lot of work.
Ok, I’ll get started on Monday.
you have a 200 node Kafka cluster just as a message queue?
here is a 2015 talk on critical messaging using pg.
https://github.com/jmscott/talk/blob/master/ams-pgopen-20150...
Replacing redis with PG for example feels very "when you have a hammer everything looks like a nail" to me.
Kafka is well documented and understood. Your custom postgres solution is not.
Best case you write high quality maintainable code and have a single dependency. Worst case is you create a complete mess that takes months to onboard people.
Right now I'm contracting on a project that uses AWS BATCH, docker, kubernetes, terraform, Nextflow, Django and postgres. It took me 2 days to on-board myself and start delivering features because everything was standard.
The original article is rebranding "Not invented here" as "Minimise dependencies"
Or at least we'll have to agree on the definition of standard here. For the equivalents of SQL, I'd say we have AMQP or MQTT, and kafka does not seem to support any (my goggling tells me it has its own protocol); deployment-wise, it doesn't seem easier to set up than, let's say, SQS; while used and supported in most common languages, most queue/job frameworks do not seem to support it OOTB (looked into ruby, python, php, javascript), so you'd have to write your own custom kafka handling code from scratch. So what would make it easier than just riding postgres?
Custom means that you wrote a unique solution for yourself. No-one else uses your solution. New hires have to learn it from scratch either by reading your code or your own custom documentation.
There is obviously more work to onboard people onto a custom solution. You can hire people who understand the standard solutions.
Postgres isn't a queue or a message broker. You are writing your own queue that depends on postgres. Your custom queue will have complexity that new hires have to learn. No new hire will have used your custom in house queuing system before.
If you want to use SQS that's a great idea. I support using standard solutions.
But I'm on a single medium sized Linode and doing full-text search over 120M small (< 1KB) to medium (< 10KB) text documents and it's still so fast I'm not sure that I will ever need to consider an alternative.
Working replication - barely and still awful 2 decades after MySQL
In place upgrades - nope
Access control that doesn't require root access to filesystem - nope
The list goes on...
Disclosure: I do postgres in production for 10000s of machines in like 49 countries, I understand how databases work and have been doing this for 20 odd years, it's a bad choice because postgres is written by purists, not people who deal with it in the real world.
You run things at an unusual scale, you have unusual problems.
Postgres still lacks basically everything that other replication implementations do, if they have to mention log shipping by rsync etc they've already got it wrong.
For clarity they only recently figured out "streaming" replication which again, is what everyone else has been doing for years.
Normal access control can be done with GRANTs, just like MySQL or Oracle.
As for streaming replication, that was added in PostgreSQL 9.0, six major versions ago. Sure, it was late to the party, but it’s not exactly a recent addition.
Regardless being 2 decades late to the party is unacceptable - it's still not complete and makes real world management a chore, I still admire the purist attitude it has its place but you can't also then claim it's a useful competitor - which is what they do.
Unfortunately it's a perl postgres vs the world thing - both are irrelevant.
As for the config, one might argue that reconfiguring replication isn’t a common task, usually something you do when setting up the cluster and never mess with again.
I understand that there are pain points running PostgreSQL at massive scale. But 99.9% of projects needing a database never reach that scale. And once they do, they probably need to re-engineer big chunks of the application anyway.
Your knowledge of what Postgres supports and how to administrate it seems to be a few years out of date. There's plenty of examples in this thread alone.
Correction: thirteen major versions ago, approx. thirteen years ago. 9.x were all major releases.
Replication can't (unless it's changed recently) be granted via ddl, and isn't propagated either - but otherwise sure
It's not just that though, the general behaviour is counter to how real world deployments work, like for example basically anyone at scale moving away to something else because the admin and operational overhead is way too much
It can, and is, at least as of the five year old (and now unsupported) release 10.
https://www.postgresql.org/docs/current/warm-standby.html#ST...
You're the one bursting in here shouting "Postgres sucks!" "based entirely" on a lack of features added more than "a decade ago". You have your choices, we have ours. You'd rather have the convenience of features, even at the risk of losing your data; we'd rather have the peace of mind of knowing our data is safe, and build features that don't exist out of whatever else we have, however we can.
It's not always about what bugs or features exist or don't exist now. It's also about the nature of a project and how it's developed, and how it's going to evolve or otherwise behave in the future. After all, we're talking about databases here; we trust our data to them.
- You can easily do prerendering and asset packing in the frontend build step
- You don't need MVC on the backend
- You can use a third party for authorization
- Frontend can just be a React app
- VirtualDOM doesn't add any complexity in addition to the frontend View
- You don't need MVC on the frontend
- Frontend can fetch data from the API and cache it with cache-control header
The simplest setup is actually to host a React app from an S3 bucket and use Lambda / Cloud Functions to respond to network requests.
You'd have to pry my monolith-on-a-vps out of my cold dead hands before you can call that the simplest setup.
Use DynamoDB for everything? Use SQLite + S3 for everything. Heck use Redis for everything.
I understand that Postgres is insanely flexible. It supports pretty much any type of data storage you want.
But if you’re going to focus all your developer knowledge on on product, consider whether another DB might be better.
How does this stack compare to Snowflake or Redshift?
By tuning the chunk sizes so their data fits in memory, many common queries gain a lot of efficiency. It's built around some assumptions of time-series data: Most inserts and queries are for recent data and are generally ordered.
I've had great experience with TimescaleDB for small-medium time-series loads such as sensor or analytics data; I've found it's pretty plug-and-play and have used it to store tables with ~1B time-series rows of geospatial data, sensor values, etc.
I really like DynamoDB. Its functional overlap with a fully-featured relational database is very narrow though. No graph queries or joins or advanced data validation or...basically anything on this list: https://www.sql-workbench.eu/dbms_comparison.html
Postgres can be made to work like DynamoDB: simple queries with no joins, table partitioning and tablespaces for I/O parallelization, etc.
DynamoDB barely scratches the surface the other way around. PartiQL is the limit and it's a very basic limit.
This might change in the future with the zHeap storage backend.
Btw, your comment strikes me like saying "I don't want to iterate with a strongly typed language".
Just nominally typed isn't my thing.
Eg. try inserting a string into an int column and watch it complain. It even supports custom types out of the box, including enums.
Probably you can use Magic the gathering cards as a computer since it is turing complete, but it will just be inconvenient as hell.