Live data from Hacker News

We do not use foreign keys (2016)

github.com

241–250 of 337 posts

Re: We do not use foreign keys (2016)

#241

Earlier quoted context omitted.

Seriously. "As a C developer, I never check exit codes of child processes. We can just enforce it by ensuring child processes don't have bugs"

`malloc` won't fail, right?

Not as much as developers who can't find their own coding errors might have you think!

Re: We do not use foreign keys (2016)

#242
Granted, I have never worked with a database the size of Github, so I've never had to deal with sharding issues. But, even though foreign keys can be a PITA in some situations, I prefer the DBMS to do it for me than rely on the application. Foreign keys have always kept my back. I've been burned a few times when they weren't used.

Re: We do not use foreign keys (2016)

#243

Earlier quoted context omitted.

`malloc` won't fail, right?

On Unix it won't. You can overcommit memory and only get into trouble when you actually try to page it. But not at malloc time.

Iirc that's configurable.

Re: We do not use foreign keys (2016)

#244
post #214

Earlier quoted context omitted.

Yes, and what if you don't make a foreign key?

Then you have an implicit rule that is enforced by a hope and a prayer that some junior dev never makes a mistake, your senior engineers are clairvoyant and understand every single aspect of your systems 100% with zero off-days, your code review process catches every single possible edge case (especially the edge cases that you never knew existed), your QA process is 100% and never makes mistakes, your servers never…

That's exactly my experience and viewpoint.

Re: We do not use foreign keys (2016)

#245

Earlier quoted context omitted.

Any database that would let you disable constraints on a session basis is a toy database. Such an operation doesn’t even make sense because at some point the relational integrity has to be enforced for the entire table. You can’t just have parts of a table be relationally correct. That is like saying 1 + 1 = 3. It is a completely illogical statement. However I would not at all be surprised to learn MySQL supports suc…

So are you asserting that the following products are all built on top of a "toy" database, and their engineers have no idea what they're doing: Facebook, YouTube, Wikipedia, Pinterest, Slack, GitHub, Etsy, Yelp, LinkedIn, Shopify, Dropbox, Wordpress, Wix, Tumblr, Square, Uber, Booking.com, Box, Venmo, SendGrid, Okta, SurveyMonkey, WePay, Alibaba, SoundCloud, among countless others... An alternative view is that your…

Once you are stuck with MySQL it is very, very, very hard to get an organization to switch--not only from a technical standpoint but a political one.

I bet you any competent engineer who knows their shit about DB in those companies regrets using MySQL. I bet their code is full of hacks, crappy schemas, and all kinds of work arounds because they chose mysql. I've seen it in every company that uses MySQL. The lengths people go to avoid schema changes is astonishing.

It is much, much better to start with a real database like PostgreSQL because whatever you pick is going to be what your entire org uses from now until eternity.

Re: We do not use foreign keys (2016)

#246
post #65

I have been perpetually annoyed at the SQL/RDBMS/relational calculus model. It always feels like a huge context shift from imperative programming. after many years of writing SQL, I noticed that many other people end up writing SQL statements that look more or less like computer programs (CASE statements, subselects, etc). It all came to a head when I naively asked an experienced SQL developer how to represent a tree…

> It all came to a head when I naively asked an experienced SQL developer how to represent a tree in SQL and learned one way to do it was to have CHILD nodes with FK references to PARENT nodes. So any time you want to get all the CHILDREN of a PARENT you have to query all children to see if they have a FK to the appropriate PARENT.

One far-less-painful solution: use what's called a "closure table" to track the ancestor-descendant relationships not just at the parent-child level, but also grandparent-grandchild, greatgrandparent-greatgrandchild, etc.

For example (assuming Postgres, and eliding some NOT NULL constraints for readability):

    CREATE TABLE node (
        id INTEGER PRIMARY KEY,
        parent_id INTEGER REFERENCES node
        name TEXT
    );
    
    CREATE TABLE node_closure (
        ancestor_id INTEGER REFERENCES node,
        descendant_id INTEGER REFERENCES node,
        depth INTEGER,
        PRIMARY KEY (ancestor_id, descendant_id)
    );
    
Then, whenever inserting a new node (assuming that Postgres allows specifying integer primary key values on insert, which I don't recall if it restricts by default):

    -- First node
    INSERT INTO node VALUES (0, NULL, "foo");
    INSERT INTO node_closure VALUES
        (0, 0, 0); -- self
    
    -- Second node (child of first node)
    INSERT INTO node VALUES (1, 0, "foobar");
    INSERT INTO node_closure VALUES
        (1, 1, 0), -- self
        (1, 0, 1); -- parent
    
    -- Third node (child of first node; sibling of second node)
    INSERT INTO node VALUES (2, 0, "foobaz");
    INSERT INTO node_closure VALUES
        (2, 2, 0), -- self
        (2, 0, 1); -- parent
    
    -- Fourth node (child of second node, grandchild of first node)
    INSERT INTO node VALUES (3, 1, "foobarbaz");
    INSERT INTO node_closure VALUES
        (3, 3, 0), -- self
        (3, 1, 1), -- parent
        (3, 0, 2); -- grandparent
The upside is that it's now trivial to query for a node and all its descendants:

    SELECT descendant.name
    FROM node AS descendant
    JOIN node_closure ON descendant.id = node_closure.descendant_id
    JOIN node AS ancestor ON ancestor.id = node_closure.ancestor_id
    WHERE ancestor.name = 'foo';
Or more succinctly (if you already know the ID):

    SELECT name FROM node JOIN node_closure ON node.id = node_closure.descendant_id
    WHERE node_closure.ancestor_id = 0;
Either of which gives you:

    ----
    name
    ----
    foo
    foobar
    foobaz
    foobarbaz
As part of this upside, since you're not having to loop through every level of ancestry, reads for deeply-nested descendants are much faster.

The downside is that you have to do extra inserts to an extra table. The inserts themselves can be automated by adding a trigger on node which automatically creates/updates/deletes node_closure rows as necessary (which is why you'd still want the parent_id in the main table: so that the trigger can grab that and build the closure rows, and so that if the closure table gets out-of-sync you can fall back on that and rebuild it), but that still leaves a performance impact on writes (pretty negligible for shallow descendants, but it gets worse for deeper ones).

Personally, I'd opt for a closure table if I know that reads are going to be more common than writes. If writes are more common than reads, then sure, some sort of crazy recursive query might be preferable.

Re: We do not use foreign keys (2016)

#247
post #25
post #5

When posts like these come up, I'd like to remind people that context matters when making technical decisions. What works for large companies with huge scale (GitHub, Google, Facebook) may not work for you. As a counter point to the linked issue, I operate a few small applications. Foreign-keys (and constraints in general) are great at ensuring that invalid data doesn't find its way into your database. Yes, they have…

In the same vein, I'd like to remind people that you are probably not a "temporarily low-scale big-data company", in the same vein as a temporarily embarrassed millionaire. In lots of cases going for the very long term scalable solution will be an impediment to your growth, and I'd suggest dealing with those issues when the chance that you need them is on the horizon, rather than across the globe. CQRS is one of the…

>In lots of cases going for the very long term scalable solution will be an impediment to your growth, and I'd suggest dealing with those issues when the chance that you need them is on the horizon, rather than across the globe.

My attitude is:

"Man if this takes off and I have to make some changes on how we do this....I should celebrate!"

Re: We do not use foreign keys (2016)

#248
post #5

When posts like these come up, I'd like to remind people that context matters when making technical decisions. What works for large companies with huge scale (GitHub, Google, Facebook) may not work for you. As a counter point to the linked issue, I operate a few small applications. Foreign-keys (and constraints in general) are great at ensuring that invalid data doesn't find its way into your database. Yes, they have…

I will add to the pile of agreements on this.

Also, if you do end up sharding a relational database, it is often by identifying subgraphs of document-like structures that you can shard on. Need to ensure these subgraphs do not have foreign key relations with one another, but you can maintain the valuable foreign key relations WITHIN the subgraphs.

Practical example: database of user profiles where suddenly you have billions? You can skill keep foreign keys on user to email-addresses or user-to-comments while eliminating cross-user foreign keys.

I would also add a good rule of thumb: when you make the design decision to remove a database feature, you need to assume that you now need to handle that feature yourself, or your data will get corrupted. For example, when removing FK constraints, or transactional boundaries, or introduce irregular checkpointing, you now need to implement a data repair system, because your data will get broken. At which point you are probably going to end up using a change event log (your own transaction log) and a system that replays the logs in order to repair and rebuild the database.

Re: We do not use foreign keys (2016)

#249

Earlier quoted context omitted.

> Reporting and bi data might get hosed. > Account management might get hosed. LOL, if I read the data directly from the db instead of via the application's API then sure, I lose the application's guarantees. But, y'know, same might be said if I just go and read the DBs files from a sidecar shell script or something. > It puts all applications on top into a undefined state ...that's just an assumption that's false.

If you want to operate assuming your data is always corrupt because your engineers don't understand how to use the tools provided by their database.... Seems like an awful lot of work to re-invent a wheel that your DB server can solve for you. I guess that is on you though....

> If you want to operate assuming your data is always corrupt

It is not, though, no matter how much you keep shouting it is.

> because your engineers don't understand how to use the tools provided by their database....

...I'd be careful waving accusations of incompetence if I were you, it's not me or my coworkers declaring databases to be black magic.

> Seems like an awful lot of work to re-invent a wheel that your DB server can solve for you.

...try to run a car company without reinventing the wheel every few years, I dare you.

> I guess that is on you though....

OK boomer.

Re: We do not use foreign keys (2016)

#250
post #90

Earlier quoted context omitted.

Speaking in terms of theory, it absolutely is. RDBMSs aren't magic boxes, they stage data for insertion, validate it, execute that insertion - all while littering the WAL (or equivalent) with the steps necessary for the guarantee of that operation. You absolutely can write this logic into your application, at that point your application may basically be Postgres but it's possible. If you attempt this and don't exhaus…

But, if you want to ensure the data is never visible in an inconsistent state, you either need to use db-level concurrency-related features like transactions or locks; or some kind of lock or other concurrency-control features at the app level while guaranteeing the db has no clients other than your app. It seems difficult to wind up with better performance characteristics by doing this than using the higher-level ab…

Transactions are a pretty widely used RDBMS feature AFAIK.
Post reply on HN