News for hackers. Stuff that matters.
Re:PostgreSQL and UUID as Primary Key by vbezhenar on Friday July 05, 2024 @05:33PM (#40886226)
IMO using bigserial by default is wrong. Use whatever data type is appropriate. Not every table will grow to 4 billion rows and not every table will grow to even 60k rows. ID data type leaks to every foreign key referencing given table. Many foreign key usually will be indexed, so this further degrades performance. There are multiple data types for a reason.
Re:PostgreSQL and UUID as Primary Key by njtransit on Friday July 05, 2024 @06:20PM (#40886571)
Using 32 bit ints for IDs is insane in today’s world. If an attacker can control record generation, e.g. creating a record via API, then they can easily exhaust your ID space. A lot of kernel vulnerabilities stem from using incrementing 32 bit integers as an identifier. If you’re considering using 32 bits for an ID, don’t do it!
Re:PostgreSQL and UUID as Primary Key by incrudible on Friday July 05, 2024 @06:56PM (#40886784)
If an attacker can create billions of records through your API, maybe that is a problem you need to address either way.
Re:PostgreSQL and UUID as Primary Key by afavour on Friday July 05, 2024 @07:30PM (#40886986)
Conversely: if using bigserial for a primary key introduces crippling performance problems to your system, maybe that is a problem you need to address either way.
Re:PostgreSQL and UUID as Primary Key by DEADMINCE on Friday July 05, 2024 @07:53PM (#40887100)
Conversely? Who mentioned there being a performance impact?
Re:PostgreSQL and UUID as Primary Key by afavour on Friday July 05, 2024 @08:23PM (#40887245)
It’s the only reason I could think of for why you wouldn’t use a bigserial column. Maybe there is another?
Re:PostgreSQL and UUID as Primary Key by lazide on Friday July 05, 2024 @08:47PM (#40887352)
Information leakage - it leaks how many records have been created [https://en.m.wikipedia.org/wiki/German_tank_problem]
Re:PostgreSQL and UUID as Primary Key by afavour on Friday July 05, 2024 @09:14PM (#40887486)
That’s why the original comment suggested both bigserial and a separate UUID for public exposure. More to the point the person I was replying to said:

> IMO using bigserial by default is wrong. Use whatever data type is appropriate. Not every table will grow to 4 billion rows and not every table will grow to even 60k rows

The implication I took from that was that they were suggesting using serial over bigserial. My comment was pushing back on that.

Re:PostgreSQL and UUID as Primary Key by jerf on Friday July 05, 2024 @09:42PM (#40887602)
My Dream Web Framework, which for a variety of reasons was never and never will be built, has built-in functionality for obscuring IDs in some session-level map, so you can indicate through some sort of type that something is an ID and it automatically allocates some sort of randomized identifier on the way out and converts it back transparently on the way back in.

Thus, not only would DB ids in principle never show in the UI, they wouldn't even be stable between sessions.

This would protect against a number of security attacks. (I don't know what I'd do about bookmarking, though I suspect most apps have a 80/20 solution they could deploy.) DB ids going out to a user really ought to be considered at least a smell, but it's pretty ingrained into our culture.

Re:PostgreSQL and UUID as Primary Key by gregoryl on Friday July 05, 2024 @10:12PM (#40887724)
I wrote that style of session mapping for a project long ago. It was fairly easy, but a massive pain in the ass to debug. Ended up needing to record the mappings in the backend for a period of time.
Re:PostgreSQL and UUID as Primary Key by bruce511 on Saturday July 06, 2024 @12:13AM (#40888120)
I too coded that in my server. It's not hard to do, and debugging can be harder at times (but not excessively so.) As you say it's just a case of logging when debugging.

When anchors are needed I use a different column for that- not the primary index. (Usually some other unique, already-present, value like the sku or whatever.

The security implications though are substantial. I don't leak primary key info even if the PK is serialized. (These days I'm all-in on UIDS but I have older legacy systems to deal with too.)

Re:PostgreSQL and UUID as Primary Key by ako on Saturday July 06, 2024 @03:34AM (#40888785)
That wouldn't work for integration scenarios where the other system need to store the ID so it can refer to it later. How would you provide IDs for integration purposes?
Re:PostgreSQL and UUID as Primary Key by SpaghettiCthulu on Saturday July 06, 2024 @10:36AM (#40890689)
Not just integration with other services. It would completely break brower history too!
Re:PostgreSQL and UUID as Primary Key by njtransit on Friday July 05, 2024 @11:09PM (#40887927)
It’s about 100 records per second for a year and a half, or 10,000 records per second for 5 days. Both are easily achievable. As an engineer, why would you ever knowingly design such a system when it’s trivial to not have this vulnerability in the first place.

It’s like hosting an internal app at a company that contains a SQL injection. “Well, if a hacker can access this app, then that’s a problem that needs addressing either way.” Sure, that may be true, but it’s also true that you’re not a good software engineer.

Re:PostgreSQL and UUID as Primary Key by arp242 on Friday July 05, 2024 @11:47PM (#40888032)
> It’s like hosting an internal app at a company that contains a SQL injection

It's nothing like that at all because the wrong SQL injection can completely ruin people's lives due to leaking stuff it shouldn't whereas the worst an int exhaustion can do is bring some app offline. Whoopdie-doo. Not comparable at all.

And there's a reason there aren't tons of "int exhaustion attacks": because there's little point in doing so.

Re:PostgreSQL and UUID as Primary Key by lazide on Friday July 05, 2024 @11:56PM (#40888066)
So taking down the apps ability to insert any rows into the table (and hence breaking the app) isn’t going to impact anyone? Including the apps ability to make money?

This does happen and break people. You usually don’t hear about it (except on the SRE side) because it is so obvious when it happens to someone they really don’t like talking about it.

Re:PostgreSQL and UUID as Primary Key by arp242 on Saturday July 06, 2024 @05:02AM (#40889137)
I never said it's "not going to impact anyone", I said it's not comparable.

"Denial of service" is just not a security bug on the same level as "zomg my credit card is stolen". I have argued this many times before, e.g. https://news.ycombinator.com/item?id=39377795 – It's easy to knock most sites offline regardless just by flooding it with traffic, don't need long-term plans like this (which are likely to be detected before it even has effect).

> This does happen and break people

I have never heard about a deliberate attack using this method.

Re:PostgreSQL and UUID as Primary Key by fauigerzigerk on Saturday July 06, 2024 @04:44AM (#40889063)
That's not a great analogy. SQL injection is a completely binary issue. A single malicious statement that gets through can do all the damage.

Defending against denial of service attacks is a gradual, multifaceted problem that is pretty unlikely to hinge on the size of database keys.

If your system is dimensioned to serve a few hundered users then it's not going to be a whole lot more robust if it can theoretically create thousands of trillions of user records rather than just hundereds of millions.

In fact, infinite scalability is a risk in its own right because it can bankrupt you, which is arguably worse than a temporary outage.

That said, I tend to use 64 bit IDs by default as well unless there is a good reason not to (because, you know, what if I'm the next Google?!?).

Re:PostgreSQL and UUID as Primary Key by rattray on Saturday July 06, 2024 @11:36AM (#40891039)
Either of those situations should be very easy to catch and prevent with basic rate limiting and monitoring.
Re:PostgreSQL and UUID as Primary Key by lazide on Friday July 05, 2024 @11:55PM (#40888062)
It doesn’t require an attacker to create billions of rows.

All it requires is for there to be billions of rows. 32 bits is nutty small considering how little overhead there is in storing 64 bits instead.

Re:PostgreSQL and UUID as Primary Key by klooney on Saturday July 06, 2024 @12:46AM (#40888223)
If you expose an API, your customer's CI systems will probably get there eventually.
Re:PostgreSQL and UUID as Primary Key by inopinatus on Friday July 05, 2024 @05:53PM (#40886372)
Defaulting to 64-bit integers internally is to me a matter of mechanical sympathy, it has little to do with row capacity. It’s just a word size that current CPUs and memory architectures like working with.
Re:PostgreSQL and UUID as Primary Key by vbezhenar on Friday July 05, 2024 @06:46PM (#40886725)
What architecture? Both amd64 and ARM64 can work with 32-bit integers just fine.
Re:PostgreSQL and UUID as Primary Key by DEADMINCE on Friday July 05, 2024 @07:54PM (#40887107)
Not optimally.
Re:PostgreSQL and UUID as Primary Key by vbezhenar on Friday July 05, 2024 @08:26PM (#40887258)
It is not optimal to use 8-byte integers instead of 4-bytes.

CPU works just as fast with both, however your CPU cache is limited and you'll put more 4-byte integers into your L1.

I don't really understand what you want to convey. CPU is very fast with any kind of integer size. There's no performance penalty to use 1-byte integer compared to 8-byte integer. And there's performance penalty when your L1 or L2 or L3 cache is busted and you need to go to the next memory layer.

Re:PostgreSQL and UUID as Primary Key by marcosdumay on Friday July 05, 2024 @08:47PM (#40887354)
> CPU works just as fast with both

They don't. CPUs have as many issues with data alignment as with cache sizes.

Re:PostgreSQL and UUID as Primary Key by adrian_b on Saturday July 06, 2024 @03:00AM (#40888637)
1-byte integers or any other integers smaller than 8 bytes (64 bits) save space only when they are parts of arrays or structures.

As individual variables, they take exactly the same space as 64-bit integers, both in registers and in memory (i.e. in the stack used for automatic variables or in the heap used for dynamically-allocated variables), because of the alignment requirements.

Therefore it never makes sense to use other integers than 64-bit, except as members of aggregate data types.

A database table is an aggregate data type, so the use of small integers in some columns may be justified, but such types shall not be used for variables in the programs that process data from the tables, where misuse of the small integers may cause overflows and there is no advantage from using smaller integer sizes.

Re:PostgreSQL and UUID as Primary Key by rangerelf on Friday July 05, 2024 @06:34PM (#40886645)
I read your post and hear echoes of "Who would ever need more than 2 digits for the year in this timestamp column?"

Never again.

Re:PostgreSQL and UUID as Primary Key by vbezhenar on Friday July 05, 2024 @06:45PM (#40886718)
Using 2 digits for year is as wrong as using 8 bytes for year.
Re:PostgreSQL and UUID as Primary Key by riku_iki on Saturday July 06, 2024 @01:49AM (#40888405)
scale of damage is very different, so "as" is up to discussion.
Re:PostgreSQL and UUID as Primary Key by lazide on Saturday July 06, 2024 @09:54PM (#40894700)
Eh, using 2 digits for year is a lot ‘more wrong’ than 8 bytes. Since all (current) known use cases can fit in 8 bytes for the conceivable lifespan of human civilization.

2 digit years don’t even support the use case of ‘store the current year’ fully.

Re:PostgreSQL and UUID as Primary Key by rangerelf on Monday July 08, 2024 @02:57PM (#40908259)
Don't wake me up when year 9,223,372,036,854,775,807 rolls by please.
Re:PostgreSQL and UUID as Primary Key by riku_iki on Friday July 05, 2024 @05:54PM (#40886380)
there is unlikely significant performance degradation for int vs big int, but it will be huge PITA, if 10 years later and tons of legacy code written that table will grow over 4B rows..
Re:PostgreSQL and UUID as Primary Key by sgarland on Friday July 05, 2024 @08:04PM (#40887171)
It’s double the size. 4 bytes * 2^31 (because Postgres doesn’t allow have unsigned ints, unlike MySQL) is 8.6 GB. That is quite a difference for an index, not to mention the table overhead.

You’re going to know well in advance before hitting this limit becomes an issue, and you’ll have plenty of time to either take a bit of downtime and do a column conversion, or do an online migration.

Re:PostgreSQL and UUID as Primary Key by big_whack on Friday July 05, 2024 @08:16PM (#40887211)
Postgres pads tuples to 8 bytes alignment so an indexed single-column int takes the same space as an indexed bigint. That's the usual case for indexed foreign keys.

Differences can appear in multicolumn indexes because two ints takes 8 bytes while two bigints takes 16, however the right layout of columns for an index is not always the layout that minimizes padding.

Re:PostgreSQL and UUID as Primary Key by sgarland on Friday July 05, 2024 @10:57PM (#40887878)
Postgres doesn't necessarily pad to 8 bytes; it depends on the next column's type. EDB has a good writeup on this (https://www.2ndquadrant.com/en/blog/on-rocks-and-sand/), but also here's a small example:

  CREATE TABLE foo
    (id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, iid INT NOT NULL);

  CREATE TABLE bar
    (id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, iid BIGINT NOT NULL);

  CREATE TABLE baz
    (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, iid BIGINT NOT NULL);

  -- fill each with 1,000,000 rows, then index

  CREATE INDEX {foo,bar,baz}_iid_idx ON {foo,bar,baz}(iid);

  SELECT table_name,
         pg_size_pretty(pg_table_size(quote_ident(table_name))) "table_size",
         pg_size_pretty(pg_indexes_size(quote_ident(table_name))) "index_size"
  FROM information_schema.tables
  WHERE table_schema = 'public';

   table_name | table_size | index_size
  ------------+------------+------------
   foo        | 35 MB      | 43 MB
   bar        | 42 MB      | 43 MB
   baz        | 42 MB      | 43 MB
`foo` has an INT followed by an INT, and its table size is 35 MB. `bar` has an INT followed by a BIGINT, and its table size is 43 MB; this is the same size for `baz`, despite `baz` being a BIGINT followed by a BIGINT.
Re:PostgreSQL and UUID as Primary Key by big_whack on Friday July 05, 2024 @11:49PM (#40888039)
You seem to think you're disagreeing with me but afaict you're just demonstrating my point, unless your point is just about how (int, int) will get packed. That's what I meant about the column order of indexes. If you have two ints and a bigint, but you need to index it like (int, bigint, int), then you aren't gaining anything there either.

As your example shows, there is no benefit in index size (e.g for supporting FKs) in going from int to bigint for a single key. You end up with the same index size no matter what, not twice the size which was what I took your original post to mean.

Re:PostgreSQL and UUID as Primary Key by sgarland on Saturday July 06, 2024 @10:58AM (#40890810)
I misunderstood your post, I think. I re-ran some experiments with a single-column index on SMALLINT, INT, and BIGINT. I'm still not sure why, but there is a significant difference in index size on SMALLINT (7.3 MB for 1E6 rows) vs. INT and BIGINT (21 MB for each), while the latter two are the exact same size. I could get them to differ if I ran large UPDATEs on the table, but that was it.
Re:PostgreSQL and UUID as Primary Key by riku_iki on Friday July 05, 2024 @08:15PM (#40887207)
> 4 bytes * 2^31 (because Postgres doesn’t allow have unsigned ints, unlike MySQL) is 8.6 GB

I didn't get your point. When it is 2^31, you definitely need bigint already. When it is much smaller, it will be much smaller overhead.

Also, per docs (https://www.postgresql.org/docs/current/storage-page-layout....), each postgres row has 23 bytes overhead, so your transition from 8->4 bytes will brings marginal improvement.

Re:PostgreSQL and UUID as Primary Key by sgarland on Friday July 05, 2024 @11:01PM (#40887893)
Not all tables need even the capacity for 2^30 rows, much less 2^31, or 2^63. If you have a reference table with things like timezone information, color schemes, etc. and are using anything other than a SMALLINT (2^15), you're probably wasting space.

As to the maximal 8.6 GB mentioned, that's not nothing, _especially_ for RAM. Disk is cheap, but RAM isn't. If you have a smaller instance – say, an r6i.xlarge on AWS (4 vCPU, 32 GB RAM), that's 1/4 of the entire DB's memory.

Re:PostgreSQL and UUID as Primary Key by riku_iki on Friday July 05, 2024 @11:09PM (#40887924)
so, what about my argument that PG has 23 bytes overhead per row and your space win is very small compared to that overhead?
Re:PostgreSQL and UUID as Primary Key by nirvdrum on Saturday July 06, 2024 @02:06AM (#40888458)
Is that an innate property or a current implementation detail?
Re:PostgreSQL and UUID as Primary Key by riku_iki on Saturday July 06, 2024 @02:11AM (#40888472)
price for various features, like ACID.
Re:PostgreSQL and UUID as Primary Key by sgarland on Saturday July 06, 2024 @09:51AM (#40890471)
It’s the current implementation as of PG 8.2, I think. It’s [0] been there for a long time, in any case, and is unlikely to change in the near future.

[0]: https://www.postgresql.org/docs/current/storage-page-layout....

Re:PostgreSQL and UUID as Primary Key by sgarland on Saturday July 06, 2024 @09:48AM (#40890454)
It’s orthogonal, and also technically incorrect – the row overhead itself is indeed 23 bytes (less NULL bitmap), but it gets aligned to 24 bytes. Run pg_column_size(ROW()) if you’d like to check.

The fact that this overhead exists has nothing to do with the 8.6 GB of wasted space on the INTs.

Re:PostgreSQL and UUID as Primary Key by riku_iki on Saturday July 06, 2024 @11:42AM (#40891084)
> It’s orthogonal

I disagree its orthogonal and explained why. I guess lets agree on disagree.

Re:PostgreSQL and UUID as Primary Key by arp242 on Saturday July 06, 2024 @12:10AM (#40888116)
With multiple tables and the same IDs being references more than once, this kind of stuff can really add up.

For example I have a table that has about a billion rows and uses bigserial, but that table references about 6 other much smaller tables that use serial. I'm saving 48 bytes per row, or 90GB in total. It's a fairly significant save, and that's just on this one table: I have a bunch of tables like this. I had bigint'd everything my disk space would be roughly double. And all of that is not even considering the extra index size and memory requirements.

Using bigint here would add absolutely nothing. I'm never going to have billions of users. I'm never going to have billions of different operating systems. There will never be billions of countries. I can probably get away with smallint for some of those, but I felt the extra headroom for that was worth the extra bytes.

This is why "bigint by default" is just bad advice IMHO. You will always need to think about it. Of course you shouldn't prematurely optimize integer sizes, but it's also not true that it doesn't matter, because it does. "Better safe than sorry" also applies in the other direction: "zomg we're wasting tons of disk space and spend much more money on servers than we need to" just because someone unthinkingly applied some stupid dogma they read on HN.

Re:PostgreSQL and UUID as Primary Key by riku_iki on Saturday July 06, 2024 @12:56AM (#40888247)
> I'm saving 48 bytes per row

you saving 24 bytes per row: downsizing 6 columns from 8 bytes to 4, which is fraction of your table size. If your system is sensitive to such change, you likely should optimize something else.

> Using bigint here would add absolutely nothing. I'm never going to have billions of users. I'm never going to have billions of different operating systems.

I think you cherry picked some fictional examples which you won't combine in one table, and your real entities are different. It adds process safety: you don't need to play mind quizzes to estimate how system can grow/change in a N years.

Re:PostgreSQL and UUID as Primary Key by arp242 on Saturday July 06, 2024 @01:14AM (#40888298)
> cherry picked some fictional example

What an incredibly rude and dismissive accusation. Here's my table: https://github.com/arp242/goatcounter/blob/master/db/schema.... – number of IDs is actually 7, not 6.

I can give a lot more details and context on all of that and why it works the way it works and the savings are certainly not insignificant and theoretical, but save me real money in server costs every month. But I have little interest in talking to you further since you're just going to insult everyone who tries to inject the slightest nuance into "always use bigserial"-extremism. So good day to you.

Re:PostgreSQL and UUID as Primary Key by riku_iki on Saturday July 06, 2024 @01:20AM (#40888318)
> https://github.com/arp242/goatcounter/blob/master/db/schema.... - number of IDs is actually 7, not 6.

the point is that some/many of those ids are not timezone and country, and potentially can grow high in cardinality.

Re:PostgreSQL and UUID as Primary Key by arp242 on Saturday July 06, 2024 @04:34AM (#40889021)
No, my point is you accused me of lying at the drop of a hat for no good reason, and that this is demonstrably not true. Don't try and spinelessly slime your way out of that. And no, they I won't run out of IDs for them. "Potentially" anything can happen, but it won't happen here. After five year the furthest sequence is 0.86% on its way of being full.
Re:PostgreSQL and UUID as Primary Key by riku_iki on Saturday July 06, 2024 @11:31AM (#40890995)
> my point is you accused me of lying at the drop of a hat for no good reason

I said you described fictional scenario, and it appears to be true, your table ID fields are very different than timezone, country and user_id.

Re:PostgreSQL and UUID as Primary Key by sgarland on Saturday July 06, 2024 @10:18AM (#40890616)
I’d just like to congratulate you on perhaps the nicest “fuck off” I’ve ever read.

Unrelated, I quite enjoyed reading your blog posts. Cheers!

Re:PostgreSQL and UUID as Primary Key by sgarland on Saturday July 06, 2024 @10:10AM (#40890588)
> If your system is sensitive to such change, you likely should optimize something else.

This isn’t even optimization, it’s just understanding your tools and needs. It’s akin to profiling your app under load, seeing that at worst it needs 1 GiB of RAM allocated, and then giving it 8 EiB just in case.

By all means, if you can reasonably predict that a given table will near or pass 2^31 rows in the near future, just set it to BIGINT now. But as has been pointed out, most apps are never, ever going to have close to 2 billion users, or for that matter 2 billion anything. The only thing I can think of that might reasonably run up against that would be invoices for large scales, or object metadata for something like Notion.

Re:PostgreSQL and UUID as Primary Key by riku_iki on Saturday July 06, 2024 @11:33AM (#40891014)
> But as has been pointed out, most apps are never, ever going to have close to 2 billion users, or for that matter 2 billion anything. T

all computers will be fine with 640kb of ram

Re:PostgreSQL and UUID as Primary Key by paulddraper on Saturday July 06, 2024 @03:14AM (#40888701)
The problems from unnecessary bigserial is nothing compared to the pain of insufficient serial.

This is especially true for PostgreSQL which increments sequences for upserts (even if no records are inserted).

That's how I've hit 32-bit limits on tables that had only a couple million rows.

---

I would only use 32-bit for very selective items that is used in a lot of FKs, like a tenant ID.

Re:PostgreSQL and UUID as Primary Key by vbezhenar on Saturday July 06, 2024 @05:45PM (#40893547)
I didn't know about upserts incrementing sequence. That makes sense. I don't think I ever used upsert, but that's a good reason to use bigint id (or at least make update count a factor to consider), thanks for pointing out.
Re:PostgreSQL and UUID as Primary Key by mixmastamyk on Saturday July 06, 2024 @03:41AM (#40888812)
Postgres doesn't support unsigned ints last I looked, so it's actually a ~2 billion limit.

Secondly, it's not XXXk rows currently-you have to consider the complete lifetime of the table. When rows are deleted/created and how often.

So what you've said is true, but the set of appropriateness for smallint is a much smaller than expected.

Re:PostgreSQL and UUID as Primary Key by blackenedgem on Saturday July 06, 2024 @01:35PM (#40891859)
You can start the sequence at -2b, or wrap it around when it gets close to the signed limit. Hopefully you haven't depended on it not wrapping around by that point.

For queue tables you can even use `CYCLE` to do that automatically.

Re:PostgreSQL and UUID as Primary Key by orf on Saturday July 06, 2024 @08:06AM (#40889915)
This is simply bad advice. There are very, very few cases where you shouldn’t use a bigserial/bigint as a primary key. Perhaps the only case is where it is impossible for the rows to grow beyond a specific bound: an “AWS region” table or a “countries” table. These are almost always lookup tables of some kind, and the domain makes it impossible for it to grow past a certain bound.

Outside of that, put absolutely no thought into it and pick a bigint/bigserial.

Trying to be clever about this ignores the fact that tables and their usage change, and what you believe now will not be correct later.

And when you realise you’ve been too clever, it’s 3am on a Sunday, your production database has exploded and nothing works. You now need to rewrite 30 tables and their foreign keys ASAP with upper management breathing down your neck.

Re:PostgreSQL and UUID as Primary Key by Merad on Saturday July 06, 2024 @12:01PM (#40891258)
An int PK does not give you 4 billion rows. For example, in Postgres every time an insert transaction has to rollback, the id's that would have been used by that insert are discarded and gone forever. Likewise people often don't realize that Postgres' very convenient upsert syntax (insert on conflict do update) will consume id's from the sequence _every time it runs_. If you do an upsert with 100 rows, it will peel off 100 new id's just in case they're needed... if they aren't used, they're discarded. This can chew through 4 billion id's really, really fast.

Personally I would only use an int PK in a table where you know the bounds of the data with very high confidence. Like, at my last job the software involved working with our client's physical store locations. If we managed to capture the entire North American market for the type of business that we worked with, we'd be looking at about 50k stores.