News for hackers. Stuff that matters.
Re:Soft deletion probably isn't worth it by dafelst on Tuesday July 19, 2022 @03:07PM (#32156439)
Views are a simple solution to this problem. Pretty much all moderns RDBMSs support updatable views, so creating views over your tables with a simple WHERE deleted_at IS NULL solves the majority of the author's problems, including (IIRC) foreign key issues, assuming the deletes are done appropriately.

I feel like a lot of developers underutilize the capabilities of the massively advanced database engines they code against. Sure, concerns about splitting logic between the DB and app layers are valid, but there are fairly well developed techniques for keeping DB and app states, logic and schemas aligned via migrations and partitioning and whatnot.

Re:Soft deletion probably isn't worth it by pbardea on Tuesday July 19, 2022 @03:20PM (#32156608)
> assuming the deletes are done appropriately

This is one gripe I have with soft-deletion. Since I can no longer rely on ON DELETE CASCADE relationships, I need to re-defined these relationship between objects at the application layer. This gets more and more difficult as relationships between objects increase.

If the goal is to keep a history of all records for compliance reasons or "just in case", I tend to prefer a CDC stream into a separate historical system of record.

Re:Soft deletion probably isn't worth it by dspillett on Tuesday July 19, 2022 @05:31PM (#32158250)
> Since I can no longer rely on ON DELETE CASCADE relationships

Cascaded deletes scare me anyway. It only takes one idiot to implement UPSERT as DELETE+INSERT because it seems easier, and child data is lost. You could always use triggers to cascade you soft-delete flags as an alternative method, though that would be less efficient (and more likely to be buggy) than the built-in solution that cascaded deletes are.

If you look at how system-versioned (or “temporal”) tables are implemented in some DBMSs, that is a good compromise. The history table is your audit, containing all old versions of rows even deleted ones, and the base table can be really deleted from, so you don't need views or other abstractions away from the base data to avoid accidentally resurrecting data. You can also apply different storage options to the archive data (compression/not, different indexes, ... depending on expected use cases) without more manaully setting up partitioning based on the deleted/not flag. It can make some query times less efficient (you need to union two tables to get the latest version of things including deleted ones, etc.) but they make other things easier (especially with the syntactic sugar like AS AT SYSTEM_TIME <when> and so forth) and yet more things are rendered possible (if inefficient) where they were not before.

> I tend to prefer a CDC stream into a separate historical system of record.

This is similar, though with system versioned tables you are pretty much always keeping the history/audit in the same DB.

---

FWIW: we create systems for highly regulated finance companies where really deleting things is often verboten, until it isn't and then you have the other extreme and need to absolutely purge information, so these things are often on my mind.

Re:Soft deletion probably isn't worth it by michaelt on Tuesday July 19, 2022 @06:04PM (#32158583)
> It only takes one idiot to implement UPSERT as DELETE+INSERT because it seems easier, and child data is lost.

Seems unfortunate to miss out on all the referential integrity benefits of a serious database when hiring standards, training and code reviews should all be preventing idiotic changes.

If I’m making a shopping cart system, I want to know every order line belongs to an order, every order belongs to a user and so on. Anyone who can’t be trusted to write an update statement certainly can’t be trusted to avoid creating a bunch of orphan records IMHO.

Re:Soft deletion probably isn't worth it by mlyle on Tuesday July 19, 2022 @08:08PM (#32159643)
> Seems unfortunate to miss out on all the referential integrity benefits of a serious database when hiring standards, training and code reviews should all be preventing idiotic changes.

If you don't use ON DELETE CASCADE, the actual foreign key constraint gives you a meaningful error-- that you need to delete some stuff to have referential integrity.

ON DELETE CASCADE --- you're telling it "eh, if you need to delete some stuff to avoid an error, go ahead, don't bother me, do what I mean".

Re:Soft deletion probably isn't worth it by dspillett on Wednesday July 20, 2022 @05:35AM (#32163370)
> Seems unfortunate to miss out on

You don't lose any referential integrity without cascades. Foreign keys are still enforced, just with an error if an action would break integrity rather than automatic deletes to satisfy the constraint that way.

> when hiring standards, training and code reviews should all be preventing idiotic changes

I was burned by this sort of thing early on, in companies where I had few such luxuries. Even though things are done better now, I'm still paranoid of that one day someone skips procedure and somehow lets a problem through all the QA loops.

> If I’m making a shopping cart system, I want to know every order line belongs to an order, every order belongs to a user and so on.

I take it from the other side: I want to know that if something is referred to elsewhere it can't be deleted until that is resolved.

If a top-level manager leaves I want an error if the hierarchy hasn't been updated before deleting his record¹, rather than his underlings, their underlings, their underling's underlings, … , being deleted when that one person is!

----

[1] Obviously this would normally be a soft-delete, there may be a lot of records referring to such an individual not just other person records. If you actually need to delete them (right to be forgotten etc.) then you need to purge the PII but keep the record so other things still hang together.

Re:Soft deletion probably isn't worth it by wvenable on Tuesday July 19, 2022 @04:47PM (#32157739)
Often you don't have to rely on ON DELETE CASCADE relationships. Because you are never deleting anything, you will never have any orphaned records. If you don't want to see say Invoices for a deleted Customer then that's just another filter feature.

Mostly I use soft-delete because for auditing requirements we pretty much can't remove anything but also because nothing ever truly goes away. If we have an Invoice or Order then, from our perspective, we must have those forever even if the corresponding client is deleted and can never place another one.

Re:Soft deletion probably isn't worth it by nkozyra on Tuesday July 19, 2022 @06:25PM (#32158789)
> Often you don't have to rely on ON DELETE CASCADE relationships. Because you are never deleting anything, you will never have any orphaned records

Exactly. Unless you're doing something silly like adding deleted at to bridge tables ... which, you probably don't need even in 1:many.

Re:Soft deletion probably isn't worth it by dexwiz on Tuesday July 19, 2022 @03:25PM (#32156674)
You may end up doing this anyways if you have any application code that needs access to delete hooks, or access control varies across objects. At this point, you are probably using a ORM instead of direct queries, and place logic that could be in the db instead at the app layer.
Re:Soft deletion probably isn't worth it by danielrhodes on Tuesday July 19, 2022 @04:23PM (#32157443)
Being unable to effectively use foreign key relationships is definitely a downside of using soft deletes. But it's also worth asking if these types of behaviors, which would also include a feature like triggers, really belongs in a database or whether it's better to have at the application level (or at least at a layer above the data layer). I'd argue that ultimately you probably don't want these things at the DB level because you get into a situation where you're sharing business logic between two (or more places).
Re:Soft deletion probably isn't worth it by mmerlin on Tuesday July 19, 2022 @05:02PM (#32157925)
My perspective is DB level triggers are the absolute very best place to put cascading update/delete logic so it only ever needs to be written once and is consistent regardless of any future frontend clients that might be written in a different language and/or framework than the original codebase.

Right now in $dayjob I am converting an old non-DRY codebase from NoSQL data layer format to proper relational SQL backend.

This old front-end was created by verbosely coding up a relational cascading update/delete system for the NoSQL backend, in numerous places redundantly with subtle differences and inconsistencies, making the code brittle.

My current estimate is some front end functions will be reduced in LOC size by 95% once we use the power of SQL in the backend.

And the backend SQL Triggers+StoredProcedure required to replace these long NoSQL frontend functions doing cascading updates/deletes is only around 10% the size of the replaced front-end code.

And now future new frontends can reuse this without exploding the size of their codebase where complex data operations are required. And no need to reinvent the same data handling algorithm all over again (and risk subtle variation creeping in from the different front-end implementation of data algorithms)

Re:Soft deletion probably isn't worth it by Volundr on Tuesday July 19, 2022 @04:45PM (#32157720)
I'm less likely to use triggers, but I'll say I pretty much always want proper foreign key relationships set up in the database. Unique and other constraints too. In principal I might agree with you that it's an application level concern, but being able to setup these kind of invariants and trust that they will be enforced even in the face of bugs in my code (and there will be bugs in my code) is just too powerful to let go of in the name of purity. I'd much rather let a bit of business logic creep between multiple layers that discover that I have a whole bunch of orphaned records and no sensible way to reconcile them.
Re:Soft deletion probably isn't worth it by corrral on Tuesday July 19, 2022 @05:07PM (#32157981)
The DB's responsible for maintaining the integrity of data in it, unless there's some very good reason you can't let it do that. It's faster and better at it than your application, 99% of the time, and it can keep doing that even when/if a second application starts using the same database, or when being used from a SQL prompt, or whatever.
Re:Soft deletion probably isn't worth it by Falkon1313 on Thursday July 21, 2022 @09:14PM (#32187319)
Presumably you have a schema defining the tables, the columns, and the types at the least, along with things like unique indexes. So you already have data constraints in your database design. And that's where they belong, to ensure the data integrity, since the database's concern is the data.

If you're doing everything as one big table of entity-attribute-value with generic blobs for the values, then yes you'll have to re-implement all the normal constraints (that the database would handle) in your application and do all your data integrity handling there. And you'll also have to duplicate that logic across every application that accesses that database now and in the future.

Data usually lives longer and has more uses than just one program. So I think it's generally better to put integrity constraints in the database, rather than having to re-implement and duplicate that logic several places.

Re:Soft deletion probably isn't worth it by munk-a on Tuesday July 19, 2022 @03:43PM (#32156936)
If we're assuming you're using a view based approach which elides the soft deleted rows automatically then you'll get a lot of these dependent objects correctly updated for free assuming you're pulling them out of the DB with JOINs - SELECT FROM foo JOIN bar (assuming bar is a view into barwithdeleted) will automatically filter out the invalid rows from foo... if you're using this information to populate a CRUD interface it's likely you'll be JOINing bar already to get some metadata for display (like maybe bar.name instead of the surrogate bar.id key you use for joining).
Re:Soft deletion probably isn't worth it by nfm on Tuesday July 19, 2022 @04:46PM (#32157734)
Yes, but other queries (any aggregate queries that don't join the soft deleted table, any joins to other tables) will now return rows that would have been deleted under hard deletion with cascade.
Re:Soft deletion probably isn't worth it by munk-a on Tuesday July 19, 2022 @04:59PM (#32157900)
This is definitely something to watch out for, but in practice (as someone that migrated a system without soft deletes to one that had them) I found that it doesn't tend to come up nearly as much as you might think - usually the table being transitioned to support soft deletes is a relatively core table (since ancillary tables usually aren't worth the complexity to transition) so a lot of your reporting queries will already be pulling in that table. You definitely need to check to make sure you're not missing anything - and sometimes CRUD interfaces will need to be completely revamped to include the table in question - but it's usually not that hard.
Re:Soft deletion probably isn't worth it by corrral on Tuesday July 19, 2022 @05:10PM (#32158028)
You could use a trigger to cascade soft-delete flag toggles, provided all the relevant tables have such a column. Still have to factor that into your other queries, but at least you wouldn't have to make potentially-impossible-to-exhaustively-check joins to figure out if a given row was "deleted".
Re:Soft deletion probably isn't worth it by kukx on Tuesday July 19, 2022 @03:34PM (#32156806)
Is not there any attempt to improve the soft deletion at the engine/SQL level? I can see it as a possible feature request.
Re:Soft deletion probably isn't worth it by smallnamespace on Tuesday July 19, 2022 @03:57PM (#32157107)
If you're using PostgreSQL, you can implement cascading soft-deletes yourself.

The information schema table holds all foreign key relationships, so one can write a generic procedure that cascades through the fkey graph to soft-delete rows in any related tables.

Re:Soft deletion probably isn't worth it by BeefySwain on Tuesday July 19, 2022 @04:35PM (#32157597)
Could someone take a stab at an example of what this would look like? Sounds really interesting.
Re:Soft deletion probably isn't worth it by NoInkling on Thursday July 21, 2022 @11:11AM (#32180340)
Here's an interesting approach using rules: https://evilmartians.com/chronicles/soft-deletion-with-postg...
Re:Soft deletion probably isn't worth it by smallnamespace on Tuesday July 19, 2022 @10:55PM (#32160811)
Re:Soft deletion probably isn't worth it by simcop2387 on Tuesday July 19, 2022 @03:47PM (#32156978)
There's the idea of temporal tables, https://pgxn.org/dist/temporal_tables/

It's not a standard (I think) but it'd let you do a cascading delete and then be able to go and look at the old objects as they were at time of deletion too.

You'd need to do things very differently to show a list of deleted objects though.

Re:Soft deletion probably isn't worth it by dspillett on Tuesday July 19, 2022 @05:38PM (#32158317)
> temporal tables … It's not a standard

They were introduced in ANSI SQL 2011.

How closely implementations follow the standard I don't know, but something close exists in several DBMSs: I use them regualrly in MS SQL Server, there are plug-ins for postgres, MariaDB has them, and so forth.

Re:Soft deletion probably isn't worth it by psYchotic on Tuesday July 19, 2022 @05:53PM (#32158466)
It appears that there's been an attempt at standardizing temporal features in SQL in the SQL:2011 standard: https://en.wikipedia.org/wiki/SQL:2011
Re:Soft deletion probably isn't worth it by simcop2387 on Tuesday July 19, 2022 @09:06PM (#32160036)
Neat I didn't know that had happened. I can't say I follow SQL standards all that thoroughly.
Re:Soft deletion probably isn't worth it by sa46 on Wednesday July 20, 2022 @01:00AM (#32161604)
You lose traditional FK constraints with temporal tables since there's multiple copies of a row. One workaround is to partition current rows separately from historical rows and only enforce FK constraints on the current partition.
Re:Soft deletion probably isn't worth it by pbardea on Tuesday July 19, 2022 @05:07PM (#32157989)
One interesting feature that some DBs implement is something like SELECT AS OF SYSTEM TIME (https://www.cockroachlabs.com/docs/stable/as-of-system-time....) which _kinda_ does this.

However in practice this usually dramatically slows down reads if you have to constantly skip over the historic rows so you probably don't want to keep garbage round longer than absolutely necessary. The concept of a historic table mentioned below could be interesting though - especially if it could be offloaded to cold storage.

Re:Soft deletion probably isn't worth it by xwdv on Tuesday July 19, 2022 @03:42PM (#32156926)
Doubt it. It seems like something obvious yet I’ve waited so long for it. Seems like you have to rely on third party plugins.
Re:Soft deletion probably isn't worth it by dragonwriter on Tuesday July 19, 2022 @06:33PM (#32158861)
> This is one gripe I have with soft-deletion. Since I can no longer rely on ON DELETE CASCADE relationships

If you use soft deletes on all tables, you can also cascade them as long as you either cascade updates to the real keys as well, or prevent such updates, by having a deleted flag column on each table, including it in a unique constraint with the actual key column(s), and including it in the foreign key.

Re:Soft deletion probably isn't worth it by semiquaver on Tuesday July 19, 2022 @03:33PM (#32156786)
The main problem with views for this use case in practice is that they ossify your schema. Views and matviews are effectively a dependency tree, and many common types of schema evolution become substantially more difficult when the system forces you to wrap your DDL in a series of view drop/recreation steps.

This is merely annoying when dealing with regular views because recreating even a large number of views is fast, but can be catastrophic if you have any matviews in your table dependency tree. A matview can easily turn what should be an instantaneous DDL operation into a partial outage while the matview is being regenerated.

(this is all postgres specific, it may be untrue for other systems)

Re:Soft deletion probably isn't worth it by enepture on Tuesday July 19, 2022 @04:17PM (#32157367)
As an FYI using a tool like DBT solves this problem. As someone who was not a data engineer I was not familiar, there were tools like this
Re:Soft deletion probably isn't worth it by teej on Tuesday July 19, 2022 @04:50PM (#32157784)
dbt is great, but I'm not sure it's appropriate to manage objects in a production transactional database. It's designed for analytical, columnar databases.
Re:Soft deletion probably isn't worth it by PaulMest on Tuesday July 19, 2022 @04:56PM (#32157859)
In case you're not familiar with DBT, it helps with the "transform" in ELT https://docs.getdbt.com/docs/introduction.
Re:Soft deletion probably isn't worth it by cpursley on Tuesday July 19, 2022 @04:34PM (#32157593)
I have this problem. What is DBT?
Re:Soft deletion probably isn't worth it by banashark on Tuesday July 19, 2022 @04:56PM (#32157857)
Re:Soft deletion probably isn't worth it by WesleyJohnson on Tuesday July 19, 2022 @09:32PM (#32160237)
Thank you for this. I had no idea this existed either and it may solve some points for us on MySQL where we've really been yearning for materialized views... which I'm now reading my be a foot gun.
Re:Soft deletion probably isn't worth it by rodw on Tuesday July 19, 2022 @07:57PM (#32159552)
> The main problem with views for this use case in practice is that they ossify your schema. Views and matviews are effectively a dependency tree, and many common types of schema evolution become substantally more difficult when the system forces you to wrap your DDL in a series of view drop/recreation steps.

You're not wrong, especially with the second part. I.e., deeply nested or convoluted dependencies between views can definitely make it awkward or painful to make adjustments near the root of the tree.

When I started this reply I was going to say "I hear you, but it's not an issue I run into very often". But that's not true. I've actually been burned by that moderately often, and have sometimes avoided or redesigned the root-level table change to avoid having to propagate all those changes to the rest of the dependency tree.

That said, in my experience (also mostly with postgres for this context) I feel like that's usually been more of a developer laziness issue (my own laziness that is), rather than an "ossified schema" issue. It's definitely a PITA when some simple change is going to break a dozen inter-connected views, but that's a coding issue not a deployment issue almost all of the time.

To be fair I don't really use matviews very often, but for true basic views I am guessing that the actual execution of the DDL to rebuild changed views is manageable in all but the most extreme cases. Even then there _should_ be a maintenance window of some sort available.

Thinking this thru a little bit, I believe the "anti-pattern" you're warning against isn't really views themselves but deeply nested/interconnected views (views that query other views, etc). I use views often (for this logical-delete type idiom for example) and I have rarely regretted it. I have often regretted creating complicated view-base dependency trees however, so I think I'm wholeheartedly in agreement on that point.

Re:Soft deletion probably isn't worth it by smallnamespace on Tuesday July 19, 2022 @04:15PM (#32157343)
To avoid an outage, have you tried fronting the matview with an additional view to allow hot-swapping?
Re:Soft deletion probably isn't worth it by slt2021 on Tuesday July 19, 2022 @04:18PM (#32157382)
it is very dangerous to have dependency on materialized view - it is a poor architectural decision from DBA to do that.

if you want view depending on mat view - materialize it yourself in a table, and refresh it yourself controllably.

Re:Soft deletion probably isn't worth it by semiquaver on Tuesday July 19, 2022 @06:41PM (#32158932)
Aren’t you just saying that materialized views should not be used? I’m pretty sure that was my original point.
Re:Soft deletion probably isn't worth it by rodw on Tuesday July 19, 2022 @05:40PM (#32158334)
Seriously. That "Downsides: Code leakage" point is nonsensical.

``` CREATE OR REPLACE VIEW active_customer AS SELECT * FROM customer WHERE deleted_at IS NULL OR deleted_at <= NOW() ; ```

There, I fixed it.

Just use `active_customer` instead of `customer ... deleted_at IS NULL`.

In fact, since the deleted_at column is a timestamp, the original "leakage" query:

``` SELECT * FROM customer WHERE id = @id AND deleted_at IS NULL; ```

is actually broken. A non-null `deleted_at` timestamp that's in the future implies the record hasn't been deleted yet, right?

I've often had junior devs assert that views are some kind of code smell, but these sorts of "canned query/filter that you want to apply very very often" seem like the perfect use case for a view to me. It's DRY, and the fact that your standard "query" is in the database" means you can change it more readily than trying to make sure you hit all the points it might be embedded in the application code.

> I feel like a lot of developers underutilize the capabilities of the massively advanced database engines they code against

Early-ish in the JDBC days a senior dev I was working with at the time (as a junior dev myself) made a pretty good case that "the database is part of the application" that's always stuck with me. Full database independence via software level abstractions is a pretty silly goal outside of library code. If you have a service that makes extensive use of the database, don't throw away the database features in the interest of some abstract "we could swap out oracle with mysql without changing anything" objective. If you want it to be generic, use the SQL standard, but don't be afraid to have a few db-specific bits in the app code if that's a subsystem you might replace once a decade or something.

I blame the DBA/Dev divide for a lot of this. A lot of the impedance between these layers is social/procedural. If you can change the DB as easily as the code, there's a lot less fear of using the right tool for the specific job.

Re:Soft deletion probably isn't worth it by djur on Tuesday July 19, 2022 @06:55PM (#32159037)
The query isn't broken. In the Rails community at least it is very common to use a nullable frobbed_at column to indicate both "was it frobbed" and "when was it frobbed". In that context, the boolean check is always NULL/NOT NULL, rather than a time comparison.
Re:Soft deletion probably isn't worth it by rodw on Tuesday July 19, 2022 @07:38PM (#32159398)
I can see that use case and I guess if that's a popular idiom within a particular dev community it's not indefensible, but from a first-principles code-complete/pragmatic-programmer kind of lens, that seems to me like a really terrible naming convention if that's your intent.

It seems to me that having a date-type column named `is_frobbed` is highly preferable to `frobbed_at` if your intent is for `IS NOT NULL` to mean frobbed.

Sure, it's a little weird to have boolean-sounding `is_frobbed` name for a date column, but the number of times that `frobbed_at IS [NOT] NULL` appears in the code is likely to dwarf the number of times you're inserting a date type into the `is_frobbed` column. I feel like there's always going to be someone (like me in this case) that's going to come across a `frobbed_at IS NOT NULL` case, notice that it's a date column and make the same flawed assumption I did. A similar thing will happen to the `is_frobbed` column too, but in that case trying to treat the date type as a boolean is going to make it obvious that you're not understanding the full context.

Frankly, if I was naming a column for this idiom and didn't have other constraints (like the Rails context), I would probably try to find a more exact and direct way to express it, if that's a little clunky. Maybe something like `frobbed_when_not_null`? But I don't love that. Honestly it may make more sense to have a actual boolean-valued `is_frobbed` column and an independent `frobbed_at` timestamp column that's populated by a trigger when the value of `is_frobbed` changes. I feel like "clunky but direct" beats out "elegant but misleading" in the long run, especially given the degree of "clunky" and "elegant" we're talking about here.

I don't expect to talk you out of it, and I'd fall in line with this in a Rails context too if that's the convention, but I think it's objectively poor design.

For what it's worth, if the `frobbed_at` convention usually intended to track the timestamp at which the frobbing happened, the good news is that my `AND frobbed_at <= NOW()` check would be unlikely to break anything in practice. Assuming that there's nothing weird going on with the timestamps, the frobbed_at dates will always be in the past anyway.

Re:Soft deletion probably isn't worth it by djur on Tuesday July 19, 2022 @07:48PM (#32159482)
I would use "deleted_after" for the use case you describe (scheduled deletions). "deleted_at" is saying that an event occurred (deletion) at a specific time. There is never going to be a case where you have a deletion time for a record that is not deleted, nor should there ever be a time where a record is deleted but a time is not recorded. So a single timestamp column is a parsimonious solution.
Re:Soft deletion probably isn't worth it by rodw on Tuesday July 19, 2022 @08:06PM (#32159621)
I agree `deleted_after` is a more appropriate name than `deleted_at`, but it starts to run into naming conventions again. For example `published_at` is often used in editorial systems (newspapers, blogs, etc.) for things that "embargoed until" some future date. `published_after` or even just `pub_dt` would probably be more appropriate, but I feel like the `_at` suffix is well established in some contexts.

For what it's worth, I have built and managed systems where "delete/disable this thing in the future" is a valid use case, but I'm wondering whether/how often/how the date aspect of the Rails-style `deleted_at` is actually used. Does anyone ever care when the record was deleted, or is it just extra metadata that is occasionally used in an ad hoc way for debugging or diagnostics. If the typical rails app replaced `deleted_at` with boolean-valued column, would it actually matter?

Re:Soft deletion probably isn't worth it by djur on Tuesday July 19, 2022 @11:28PM (#32161057)
I have relied on *_at timestamps countless times, for support, analytics, etc. deleted_at in specific? Less often, sure.
Re:Soft deletion probably isn't worth it by doctor_eval on Tuesday July 19, 2022 @06:49PM (#32158996)
> the database is part of the application

100% this. If you accept that the database is part of the application, you give yourself permission to use the full feature set of the database, and life becomes a lot simpler. Using views, stored procedures and other features lets you implement things like soft delete trivially, without it infecting all your application code.

In my entire career I've changed backend databases for an application exactly twice. It's not easy, and no amount of abstraction is likely to make it easier.

Re:Soft deletion probably isn't worth it by dubswithus on Tuesday July 19, 2022 @09:33PM (#32160248)
> Using views, stored procedures and other features lets you implement things like soft delete trivially, without it infecting all your application code.

That’s great but some of us actually like to write code. Especially Ruby on Rails where soft delete is a breeze if you don’t overthink it and build something the business doesn’t need.

Re:Soft deletion probably isn't worth it by doctor_eval on Wednesday July 20, 2022 @03:32AM (#32162643)
Well, less code means less bugs, but go nuts. I'm just saying that the database is part of the stack. You wouldn't avoid Ruby features "just in case we stop using Ruby" - so why would we avoid using database features? It's up to you how best to assemble the features from your stack.

There is a reason we avoided database features in the 90's, which was to avoid database vendor lock-in. This was almost entirely a financial decision - you didn't want to be at the mercy of a vendor who could 10x the license fees and put you out of business, so you needed to have leverage over the vendor, and you needed to be able to credibly say you could change databases -- and in fact this exact scenario actually happened to me.

But that kind of behaviour is not really needed any more, and there's no need to avoid the great database features that systems like PG provide. In my experience, using database primitives to implement features tends to perform much faster (10x - 100x) and more consistently than the equivalent implementations higher up the stack.

Re:Soft deletion probably isn't worth it by Falkon1313 on Thursday July 21, 2022 @10:33PM (#32187809)
The main reason was indeed vendor lock-in. Databases (like compilers) were hugely expensive back before open source got big.

But also some of the features were a bit flaky back then. I remember views in MySQL always caused problems - if you dumped and restored (ex: staging to dev), the special handling of permissions for views and such ("Access denied; you need the SUPER privilege for this operation") would break stuff. So we just didn't use them.^1

However, I'm still in favor of them, and think it's worth finding workarounds for things like that. They're not as flaky now, and people have found workarounds for the remaining flakiness. Nowadays the fear of using them is just tradition from 30 year old problems.

But now we have one or two generations of developers who were taught "Don't use the database features! 'Best practice' is to use it as a dumb datastore and re-implement them all in your application." But they don't know why, so wouldn't even know that those reasons are no longer applicable.

>^1 There exists a shortcoming with the current implementation of views. If a user is granted the basic privileges necessary to create a view (the CREATE VIEW and SELECT privileges), that user cannot call SHOW CREATE VIEW on that object unless the user is also granted the SHOW VIEW privilege.

>That shortcoming can lead to problems backing up a database with mysqldump, which may fail due to insufficient privileges. This problem is described in Bug #22062.

>The workaround to the problem is for the administrator to manually grant the SHOW VIEW privilege to users who are granted CREATE VIEW, since MySQL doesn't grant it implicitly when views are created.

> -- https://dev.mysql.com/doc/refman/8.0/en/view-restrictions.ht...

Re:Soft deletion probably isn't worth it by dubswithus on Wednesday July 20, 2022 @10:18AM (#32166115)
The use case for using the database here is overblown. I've never experienced of heard of anyone using a view for the users table. I suppose it may exist in the wild somewhere.

I wouldn't avoid Ruby features because the applications I develop are in Ruby and it would require a complete rewrite anyway. I avoid the database because there are development speed advantages to using Rails features. And everything works together nicely assuming you do things the Rails way.

https://github.com/rubysherpas/paranoia

> If a record has has_many associations defined AND those associations have dependent: :destroy set on them, then they will also be soft-deleted if acts_as_paranoid is set, otherwise the normal destroy will be called.

It's convenient.

Re:Soft deletion probably isn't worth it by doctor_eval on Wednesday July 20, 2022 @10:52PM (#32175227)
> I've never experienced of heard of anyone using a view for the users table

Well, now you have heard of people doing this.

Re:Soft deletion probably isn't worth it by madisp on Tuesday July 19, 2022 @06:06PM (#32158602)
I'd argue that the simple `deleted_at IS NULL` check is not broken - unless your product / domain specifically allows and requires scheduled future deletions adding such logic can easily introduce bugs. For example, you could to get the comparison flipped by accident, and if it's only in one place out of many that bug could go unnoticed for a while.
Re:Soft deletion probably isn't worth it by rodw on Tuesday July 19, 2022 @07:14PM (#32159210)
If any non-null value for deleted_at indicates a logical delete, it seems like TIMESTAMP is the wrong data type for that column.

I mean I guess I can imagine a scenario where you want to know _when_ a record was logically deleted in addition to a flag that says is_deleted, but maybe that should be two different columns.

Or at the very least, `deleted_at` is a misleading name in that case. Interpreting `deleted_at` as "is deleted after but not before" is definitely a reasonable _enough_ interpretation. I agree it doesn't unambiguously mean that, but it's at best ambiguous enough to make for a bad column name if `IS NOT NULL` is the way you intend to check for a logical delete.

> you could to get the comparison flipped by accident, and if it's only in one place out of many that bug could go unnoticed for a while.

That's exactly why I proposed a database view as the proper solution to both whole `deleted_at IS NULL` "code leakage" issue. If you have a table that uses a logical-delete idiom that you intend to query from a bunch of different places in the code, a database view that filters out the logically deleted rows is a much more appropriate solution. It's a universally DRY solution. The single simple view covers all tech stacks, all services, etc., up to and including interacting with the database via some sort of REPL console.

If you need to add `deleted_at IS NULL` to, say, 90% of the queries against the `customer` table and you're afraid someone is going to forget to do that somewhere somehow, it seems pretty obvious kind of "extract common WHERE clause" refactoring is warranted. This is especially true in the actual "business meaning" context here. You are only going to be interested in the logically deleted rows in small number of specific circumstances. Having some way to extract away that detail from the 90% of the use cases where "logical deleted" is meant to be functionally equivalent to "actually deleted" seems extremely warranted. It's a perfect counter-example to overcome an aversion to using database views. I get that the `active_customer` abstraction is probably awkward to implement in a lot of application code contexts (depending on your ORM/DB-query framework I guess), but the database view is a simple and elegant way to address that really clearly defined need.

Re:Soft deletion probably isn't worth it by d0100 on Tuesday July 19, 2022 @08:19PM (#32159719)
> If any non-null value for deleted_at indicates a logical delete, it seems like TIMESTAMP is the wrong data type for that column.

Not if I also want the timestamp of when it was deleted

Re:Soft deletion probably isn't worth it by rodw on Friday July 22, 2022 @02:38AM (#32189202)
That's fair. It's what I was trying to acknowledge when I wrote "I can imagine a scenario where you want to know _when_ a record was logically deleted".

That said, I'm genuinely curious about how often and in what way that time-based information is actually used.

For example, I often see created_at and modified_at columns that are completely ignored by the actual business logic and application code that are only used (if ever) for ad hoc diagnostic purposes like reconstructing the sequence of events after some catastrophic error in the app logic screwed up the database. If run-away-logic-screwing-up-the-database is a legitimate cause for concern, that's probably reason enough to include that extra meta-data, but I can easily count on one hand the number of times that's come up in practice across my long career. I mean, I also include that sort of column often. And there are certainly entities for which those data are relevant for application logic and/or reporting purposes. But there are definitely cases in which those data are never used at all too.

If this `deleted_at` timestamp-as-boolean convention is popular enough in the Rails community to be universally known, is there some common use for knowing _when_ a record was logically deleted (outside of reconstructing of the database state after something goes wrong) that I'm missing?

I.e., is this something that's actually used in the typical business logic and application code, or is it primarily used for ad hoc debugging?

To be clear I'm not _doubting_ that such a use case exists, it's just that off the top of my head I can't think of commonly occurring one.

Re:Soft deletion probably isn't worth it by dubswithus on Tuesday July 19, 2022 @09:30PM (#32160221)
Every Rails developer knows what “deleted_at” means. I’m absolutely repulsed at the thought of turning the users table into a view. What’s next? No code and we only use triggers?
Re:Soft deletion probably isn't worth it by rodw on Friday July 22, 2022 @01:40AM (#32188908)
> I’m absolutely repulsed at the thought of turning the users table into a view. What’s next? No code and we only use triggers?

Assuming there's a justifiable need to apply the logical-delete idiom to the users table, what's the Rails-native, ActiveRecord-based approach to filtering out the logically deleted rows?

Re:Soft deletion probably isn't worth it by doctor_eval on Tuesday July 19, 2022 @06:53PM (#32159022)
You don't put the check in your code, you put it in the view, and access the data exclusively through that view. In that way, the check is defined exactly once.
Re:Soft deletion probably isn't worth it by nightpool on Tuesday July 19, 2022 @07:09PM (#32159173)
And now your view is based on an unstable function, so it's impossible to write partial indexes for (very important for performance!) and impossible to use foreign keys with. Just add a check constraint that deleted at is never in the future and move on with your life. If you really, really need to schedule a delete, make it its own concern (UserVersions table with applicable_at?), don't mix it with your soft delete logic.
Re:Soft deletion probably isn't worth it by aeyes on Tuesday July 19, 2022 @03:35PM (#32156826)
At least in Postgres, having a huge amount of "dead" data in large tables is problematic because vacuum always has to read the full data set.

Even with conditional indexes where you exclude deleted data you take a significant performance hit reading dead blocks because there is no way to quickly vacuum them. You accumulate hours of bloat until your vacuum finishes.

You can't beat a separate insert only archive table which you never have to vacuum.

Re:Soft deletion probably isn't worth it by paulmd on Tuesday July 19, 2022 @07:13PM (#32159203)
This is practically the #1 use-case for partitions imo. Partitions are tables with syntactic sugar (that Postgres understands in its query planner) so partitions maintain their own indexes and are vacuumed individually. If you structure your partitions into hot/cold, or hot/cold_day1/cold_day2/etc then you get several advantages:

* hot and cold do not churn each others indexes or tables, you effectively have only one set of indexes (and data) that's actually churning and the others are stable.

* hot and cold can be treated differently - you can perform more aggressive indexing on data once you know it's out of the hot-set, or partition your hot data onto a tablespace with dedicated hardware while cold data is on bulk hardware, etc. Since queries are planned onto each partition individually, postgres can often select "the right way" for each partition.

* "deleted_at" is a special case of cold table. Dropping any partition is basically free, so if you partition into date ranges, then at the end of your data retention period you just drop the partition for that date range, which doesn't churn indexes or induce vacuuming

If data can never exit the cold-data state, then it's effectively an append-only table too, it just exists as a supplement to your online/hot-data table but it doesn't require special attention/etc. So we're in agreement on that point, that's a good design feature if you can swing it!

(note that for audit logging, I think it's simpler to just do the separate table. But the partition strategy does have some advantages for "cold" or "dead" data as a more generic concern imo)

Re:Soft deletion probably isn't worth it by anarazel on Tuesday July 19, 2022 @06:00PM (#32158533)
Vacuum does not have to read the full data set every time. The visibility map tracks, on a block level, whether all rows in a page are known to be visible to everyone (starting in 8.4 or such) and whether the page is "frozen", i.e., does not contain visibility information that might need vacuuming (starting in 9.6 IIRC).

However, indexes do currently have to be scanned as a whole. But that's only done by autovacuum if there's enough row versions for that to be worth it (in recent versions).

Re:Soft deletion probably isn't worth it by layer8 on Tuesday July 19, 2022 @03:55PM (#32157080)
Shouldn’t partitioning help with that? (I have no experience with Postgres.)
Re:Soft deletion probably isn't worth it by tomnipotent on Tuesday July 19, 2022 @05:57PM (#32158504)
Only if those partitions are on separate storage, otherwise you have the same number of dead tuples/dirty pages.
Re:Soft deletion probably isn't worth it by klysm on Wednesday July 20, 2022 @09:24AM (#32165458)
Not sure that is true, Postgres can rule our entire partitions and scan less stuff I think?
Re:Soft deletion probably isn't worth it by tomnipotent on Thursday July 21, 2022 @06:12AM (#32177554)
Doesn't help with writes. More dead tuples can mean more pages scanned for the same amount of data.
Re:Soft deletion probably isn't worth it by jaydub on Tuesday July 19, 2022 @03:18PM (#32156585)
Based on my experience, I like the author's approach since it makes things pretty clear-cut and optimized the storage in the core table (in my experience as well, deletes happen frequently and the soft deletes are rarely touched). In large, row-oriented tables that that storage can add up and even with views/materialized views there's a cost to using/maintaining those as well.
Re:Soft deletion probably isn't worth it by SoftTalker on Tuesday July 19, 2022 @03:56PM (#32157084)
A problem (unless something has changed, my context is Oracle from some time ago) is that NULL values are not indexed. So the "WHERE deleted_at IS NULL" could trigger a full table scan. It can also cause row migration when the NULL value is eventually filled in. Unless you explicitly need the deleted date, it's probably better to use a non-nullable Y/N for this.
Re:Soft deletion probably isn't worth it by remram on Tuesday July 19, 2022 @04:58PM (#32157886)
It seems Oracle does it although there is a special syntax to opt-in. That seems wild. I am not aware of another DBMS having that limitation though.
Re:Soft deletion probably isn't worth it by firloop on Tuesday July 19, 2022 @03:17PM (#32156580)
Views can really bite you performance wise, at least with Postgres. If you add a WHERE against a query on a view, Postgres (edit: often) won't merge in your queries' predicates with the predicates of the view, often leading to large table scans.
Re:Soft deletion probably isn't worth it by dafelst on Tuesday July 19, 2022 @03:19PM (#32156604)
IIRC Postgres has supported predicate push down on trivial views like this for over a decade now, and possibly even more complex views these days (I haven't kept up with the latest greatest changes).
Re:Soft deletion probably isn't worth it by firloop on Tuesday July 19, 2022 @03:21PM (#32156621)
Postgres can do it, you're correct, but in my experience it rarely happens with any view that's even slightly non-trivial even on recent versions of Postgres. Most views with a join break predicate pushdown. It greatly reduces the usecases of views in practice.
Re:Soft deletion probably isn't worth it by sarchertech on Tuesday July 19, 2022 @03:54PM (#32157068)
I haven’t had any problems with this at all and I’ve been using joins in my views for years.

Are you using CTEs in your views?

Re:Soft deletion probably isn't worth it by bavell on Tuesday July 19, 2022 @05:56PM (#32158494)
Re:Soft deletion probably isn't worth it by tomnipotent on Tuesday July 19, 2022 @05:53PM (#32158470)
Postgres 12 fixed the CTE issue.
Re:Soft deletion probably isn't worth it by AdrianB1 on Tuesday July 19, 2022 @06:03PM (#32158571)
There is no impact with views in MS SQL. You can also have indexed views and filtered indexes, so you can have even better performance.
Re:Soft deletion probably isn't worth it by 5e92cb50239222b on Tuesday July 19, 2022 @03:37PM (#32156846)
This is one of those situations where a good ORM can simplify things greatly. For example, with EF Core you can add a global filter which will filter out soft-deleted rows in all queries automatically (unless you add .IgnoreQueryFilters()).

It couples nicely with some hackery which turns removes into soft-deletes. You can remove objects as usual and they get soft-deleted in the database.

I've used this in a few projects and it's fantastic.

https://docs.microsoft.com/en-us/ef/core/querying/filters

https://www.thereformedprogrammer.net/ef-core-in-depth-soft-...

Re:Soft deletion probably isn't worth it by 7crow on Tuesday July 19, 2022 @05:08PM (#32157997)
> there are fairly well developed techniques for keeping DB and app states, logic and schemas aligned via migrations and partitioning and whatnot.

Hi, <1 yr experience swe here. Would HN mind unpacking "whatnot" with specific names of some these techniques?

Re:Soft deletion probably isn't worth it by OJFord on Tuesday July 19, 2022 @03:26PM (#32156695)
I had the same reaction to the 'code leakage' section, but 'foreign keys'? You can't reference a view; so you either don't use them (fks) or they point at the underlying table and you have the problem described.

You could have views that say 'thing I have a foreign key to is not deleted' of course, but that sort of seems like 'code leakage' again, just in SQL this time.

Re:Soft deletion probably isn't worth it by ibejoeb on Tuesday July 19, 2022 @04:53PM (#32157817)
> developers underutilize the capabilities of the massively advanced database engines

So true. There are so many amazing, powerful features in all of the major players.

Also: updatable views are amazing. With query rewriting (whatever you vendor calls it) you can affect some truly material changes to the system without any changes to the client applications. An example would be implementing temporal relations.

Re:Soft deletion probably isn't worth it by layer8 on Tuesday July 19, 2022 @03:54PM (#32157059)
How does this help with foreign keys? Normally you can’t have foreign keys referencing a view.

I agree that one should make use of RDBMS capabilities. A check constraint may be practical instead of (or in addition to) the foreign-key constraint.

Re:Soft deletion probably isn't worth it by nousermane on Tuesday July 19, 2022 @03:14PM (#32156543)
> (...updatable view...) WHERE deleted_at IS NULL

This is the way. Also, save record creation timestamp, and you can have very flexible "time-machine" selects/views of your table essentially for free.

Re:Soft deletion probably isn't worth it by moggers on Wednesday July 20, 2022 @01:04AM (#32161628)
I'm glad to see this thread. I've been mulling over this exact issue of deleted_at code leakage with a naive soft-delete implementation. My immediate thought was to use views, so its nice to see this is not yet another case of me using crack-brain ideas out of inexperience.

What's an appropriate naming convention?

Should I do it universally and put transparent views in front of all my tables so I don't have to refactor my code to point to new views whenever I do suddenly need to put in a model constraint that isn't 1:1 with my data layer? Is a transparent view a no-op in terms of perf? if it matters, this is being done in Postgres

I will probably make my constraints partial over the not-deleted records, particularly for unique constraints used for upserts. Am I about to footgun myself? Is it even necessary with the new uniqueness behavior with NULLs being implemented in postgres? Will my performance characteristics be better one way or the other in particular circumstances? It sounds like if I have a high ratio of deleted to not-deleted records a partial index becomes necessary.

Re:Soft deletion probably isn't worth it by airstrike on Tuesday July 19, 2022 @03:18PM (#32156597)
Views are such a powerful concept I’m honestly disheartened by how hard it is to use, replicate or leverage that functionality outside of dropping straight into the db shell
Re:Soft deletion probably isn't worth it by quickthrower2 on Tuesday July 19, 2022 @05:43PM (#32158371)
Re:Soft deletion probably isn't worth it by zozbot234 on Tuesday July 19, 2022 @03:34PM (#32156807)
Views can be used to implement pretty much any kind of automated inference, reasoning, rules etc. on the "raw" table data. The example of filtering out deleted records is just one of the simplest. That one single feature can easily transform a simple DB platform into a fully-featured knowledge base system, easily usable to support even complex reasoning tasks.
Re:Soft deletion probably isn't worth it by coding123 on Tuesday July 19, 2022 @05:01PM (#32157922)
I was going to chime in with this. thanks. One issue with views however is that a lot of these features require more and more nuanced knowledge of RDBMSes where these days unless you have a veteran architect, most of the team just knows the various library/tooling that interacts with "a variety of databases" so there is often less effort to go deeper.
Re:Soft deletion probably isn't worth it by quickthrower2 on Tuesday July 19, 2022 @05:46PM (#32158407)
Where is this anti-fb culture? Is it a startup thing?

Everywhere I have worked people know a decent amount about their data store. Not architects, just mid devs and higher.

Re:Soft deletion probably isn't worth it by Falkon1313 on Thursday July 21, 2022 @11:02PM (#32187972)
I've seen so much torturously complex code to coerce an ORM or query builder to generate a query that would've been simple just to write. Because "that's the way it's done" - Spend 5 minutes writing a query and then 5+ hours trying to figure out the convoluted code necessary to coerce something to generate that same query.

Of course, there are times when query builders are necessary to create the queries dynamically depending on parameters. But it's all too common to see them used and abused even for static queries.

People are just plain afraid of writing SQL. Or they think it's 'best practice' to use an ORM or query builder and using actual SQL is somehow 'wrong'.

Even people who know a decent amount about the database do it anyway because "that's the way that it's supposed to be done" or "that's 'best practice'".

Re:Soft deletion probably isn't worth it by vlunkr on Tuesday July 19, 2022 @03:26PM (#32156700)
That doesn't solve the foreign key problem. You can still easily have a reference to a record that is "deleted"
Re:Soft deletion probably isn't worth it by yen223 on Tuesday July 19, 2022 @06:00PM (#32158538)
Also in Postgres, you cannot have a foreign key constraint that references a view, not even a materialised view.

I'm with the author on this one. Any soft delete logic does have a tendency to bleed into other systems and make your systems more complicated, for very little gain.

Re:Soft deletion probably isn't worth it by chrisshroba on Tuesday July 19, 2022 @03:33PM (#32156797)
How would a view solve the foreign key issue? Are you suggesting coding specific deletion triggers into the view such that appropriate foreign keys are "cascade" deleted when a row in the view is deleted?
Re:Soft deletion probably isn't worth it by jermaustin1 on Tuesday July 19, 2022 @03:52PM (#32157041)
because when you are getting a child record through a join (in the view), the parent will never have deleted_at set.

Say I have a simple view `select * from foo join bar on foo.foo_id = bar.foo_id where foo.deleted_at is null`

I never have to worry about deleting from bar, because I should never grab a child when the parent is 'deleted'.

Re:Soft deletion probably isn't worth it by thehappypm on Tuesday July 19, 2022 @04:39PM (#32157654)
Assuming that the joins are always joined against the views, not the raw tables. This introduces an opportunity for a mistake.
Re:Soft deletion probably isn't worth it by dimgl on Tuesday July 19, 2022 @09:11PM (#32160067)
100% this. And yes, with inner joins it also solves the relationship issues.