Skip to content

Commit c323d6e

Browse files
authored
Merge pull request #47 from Amy-Franz/add-explination-for-the-problem
add explanation for the problem with atomic
2 parents cd9d886 + 751a4f8 commit c323d6e

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

docs/why.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
## Django's Atomic
2+
Django's `atomic` ensures database changes are committed together-or-not-at-all. It creates a savepoint or a transaction depending on two factors:
3+
4+
- The arguments passed to it (`durable` and `savepoint`).
5+
- If a database transaction is already open.
6+
7+
Specifically, the **Behaviours** which `atomic` exhibits are:
8+
9+
| | `durable=False` (default) | `durable=True` |
10+
| --- | --- | --- |
11+
| `savepoint=True` (default) | **A**. Begin a transaction if needed. Creates a savepoint if already in a transaction. | **B**. Begin a transaction, or throw an error if one is already open. Never creates a savepoint. (The `savepoint` flag is ignored.) |
12+
| `savepoint=False` | **C**. Begin a transaction if needed. Never creates a savepoint. | **D**. Same as **B**. |
13+
14+
Uses of `atomic` fall into three broad **Categories**:
15+
16+
1. Create a *transaction* to wrap multiple changes.
17+
2. Create a *savepoint* so we can roll back to in order to continue with a transaction after failure.
18+
3. Changes to be committed *atomically*, but not specific about where the transaction is created, as long as there is one.
19+
20+
## Problems
21+
22+
Django's atomic creates many savepoints that are never used. There are a couple of main causes:
23+
24+
1. Savepoints are created with decorators (`@atomic`).
25+
2. `atomic` creates savepoints by default. The default arguments (*Behaviour* **A**) are an [attractive nuisance](https://blog.ganssle.io/articles/2023/01/attractive-nuisances.html) because they make us create savepoints when we don't need them.
26+
> … if you have two ways to accomplish a task and one is a simple way that *looks* like the right thing but is subtly wrong, and the other is correct but
27+
> more complicated, the majority of people will end up doing the wrong
28+
> thing.
29+
> [**Attractive nuisances in software design**](https://blog.ganssle.io/articles/2023/01/attractive-nuisances.html) - [Paul Ganssle](https://blog.ganssle.io/author/paul-ganssle.html)
30+
3. We have no easy way to indicate the creation of a savepoint that doesn't have the potential to create a transaction instead. The only tool we have to create a savepoint is *Behaviour* **A**, which can create a transaction.
31+
32+
## What Subatomic implements
33+
- `transaction()`. Begin a transaction, or throw an error if a transaction is already open. Like `atomic(durable=True)`, but with added after-commit callback support in tests.
34+
- `savepoint()`. Create a savepoint, or throw an error if we're not already in a transaction. This is not in the table of *Behaviours* (the closest we have is *Behaviour* **A**, but that can create transactions).
35+
- `transaction_if_not_already()`. Begin a transaction if we're not already in one. Just like *Behaviour* **C**. This has a bit of a clunky name. This is deliberate, and reflects that it's a bit of a clunky thing to do. To be used with caution because the creation of a transaction is implicit. For a stricter alternative, see `transaction_required()` below.
36+
- `transaction_required()`. Throw an error if we're not already in a transaction. Does not create savepoints *or* transactions.

mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ extra:
3333
alias: true
3434
default:
3535
- latest
36+
markdown_extensions:
37+
- tables
3638
plugins:
3739
- search
3840
- mike

0 commit comments

Comments
 (0)