The world is full of bugs of the form "does this file exist? no? OK, open it for writing", which is exploitable by dropping a symlink in there between the check and the open.
The classic primitive CMPXCHG works like this too; you use it as you would a "store", but one that might fail if another thread has changed a value in the meantime.
It’s good practice to design APIs to make race conditions less likely, by explicitly not splitting operations across multiple calls.
Separating “exists” and “get” into separate calls is a disaster.
I guess databases and file systems are different because concurrent access is the norm, not the exception, and APIs should be designed accordingly. But for most of your in-memory structures, you probably don't want them dealing with concurrency because they aren't going to be able do that very well anyways (instead, deal with concurrency outside of the data structure and have them throw when inconsistencies from bad concurrency policies arise).
(Okay, probably not twice as slow, since the cache will be hot, but still.)
Or if you "know" it's there call the version that throws an exception if it isn't.
Then again, maybe there are cars built that way. Oy vey.
I would never expect that a call to `Record.find(...)` would always find records, and that the failure to find a record means that something is broken. The absence of data is usually all you need to handle control flow, and treating absence as a failure is presumptuous of API engineers. Having to account for a specific exception just adds more lines of code that could be easily avoided.
A better use of an exception would be for a system failure like this: "Connection Failed"
IMHO, that's being a bit too ideological. It's better to think about it in terms of how you'd want to respond to the condition, and pick your tool appropriately.
For instance, if I'm asking for a record by ID, then a record not found exception is a good fit. I was probably going to do something with the record, but now I can't, and the exception idiom tool the condition to be dealt with where it happened even if I forget.
If I'm asking for a set of records matching a criteria, and nothing matches, then a record not found exception is a poor fit. Code that can deal with a populated list usually can deal with an empty list just fine, and using an exception just adds friction.
If I'm asking for a set of records matching a criteria, and the database is is down, then an empty list is a poor fit. I have information about the connection error I need to communicate, so it's better to use an exception.
This isn't something you can make universal statements about, because it depends on context.
If the program knows it just put or saw the record there, is it strange to assume it's still there? If the program completely controls the database and something else in it implies the record is there, is it still strange to assume?
Sometimes broken assumptions can only signify that something has gone very wrong, and it's not worth handling them at a fine-grained level. That leads to the "gotta catch em all" antipattern the article mentions.
This just isn't true. It depends on what the record is, and what state you're in (aka: CONTEXT).
incredibly clear example: 1. User logs in 2. User record no longer exists 3. Exception
That's not a recoverable situation, and the client should see that as an unexpected error.
> This just isn't true. It depends on what the record is, and what state you're in (aka: CONTEXT).
These two things mean the same thing. It's in the context - > it is not an inherent property.
It's not an inherent property - > it's not always present.
Some conclude therefore it shouldn't be an exception by default.
>Anyone who expects a database to always have records and that the absence of a record is an "exception" has a very strange way of thinking.
But pedantics aside, my point stands. There are absolutely contexts and code paths where a missing record is basically unrecoverable. Why bother returning an error to the local calling context when you know that code doesn't make sense anymore and can't recover? A jump to a new code path is the right call, and exceptions do that (with a lot of nice trace information about why we ended up on that new code path to boot).
Doesn't mean a missing record should always result in an exception, but it's certainly not a "very strange way of thinking".
I don't consider looking for an invalid key exceptional, basically, so I wish the syntaxes were reversed, so I could use the more succinct form for (my) more common case.
The idiomatic way here being that the zero value should be a sane default for that value. You are then free to create your own types ontop of that, but the defaults are often in the ballpark of where you want to be unless you have specific needs in your context.
This is not always the case. In fact, for query APIs I do not expect it at all. Sometimes I'm doing something similar to a UPSERT operation with more nuanced behavior, then returning no existing entry on a preceding SELECT query could be the 99% use case.
> This is not always the case. In fact, for query APIs I do not expect it at all.
And, in your case, "if you expected the record to be found" doesn't apply.
If I'm iterating over a list of names/id provided by the APi, and then calling the API for more information each one, then I would expect the record to be found... because the API just told me it exists. In that case, it not being found is an exceptional condition.
This is why API generally have checks that allow developers to avoid that failure if the failure is expected (eg Record.exists). What you are suggesting is simply a reverse order (check after vs check before). I think check before leads to a much cleaner API than one that dumps a null or failure code instead.