Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
More Control with Idempotency (buttondown.email/krcah)
73 points by charlieirish on April 15, 2022 | hide | past | favorite | 17 comments


Idempotency is something I think about constantly. It's part of my Optimize For Sleep® development philosophy. You have to assume that 1) your program can and will fail at any arbitrary point during execution and 2) the caller of your code will retry it with reckless abandon every time it fails (or even if it doesn't). I know this because I am often on both sides of my own code, the writer and the caller. Managing state to avoid side effects takes a lot of mental effort and devs often get complacent or rush through it, but eventually it is going to burn you.


Idemopotency is up there with immutability, queues & purity for making the life of the software architect bearable.

This article glances off a really interesting related problem - the choice between “at most once”, “at least once” and “exactly once” events.

If the process is due to send an alert mail, but the process (or the server or whatever) dies just after the mail is sent but before the status is updated in the system’s state.


> This article glances off a really interesting related problem - the choice between “at most once”, “at least once” and “exactly once” events.

Yeah, I'm not a distributed systems expert, but I think this is one of the foundational problems of distributed systems. And that exactly-once delivery is considered to be impossible over a lossy channel. (That excludes abstractions used to create lossless channels over a lossy channel, such as TCP.) Hence the choice between at-most-once and at-least-once delivery, of which I think most of us choose at-least-once.


What do you mean by purity here?


Just being conscious of side effects really.

From an architecture point of view, maybe you’d like to change the space vs. time tradeoff for a component (maybe apply memoization) in response to a change of business need but that can be nasty when the component itself knows how to go lookup business date by itself or how to send a notification etc.


> A program is idempotent if when we run the program multiple times (on the same input), it will have the same effect as if we run it only once.

Determinism/purity could be defined the same way; the difference hinges on your interpretation of "run the program multiple times on the same input". That is, if you run `f(x)` and then `f(x)` again (on the same input, see!), you ought to get the same result. Given the spotlight pure functions have received for the last N years, maybe this distinction is worth making explicitly?

The "on the same input" part is, I think, what puts it uncomfortably close to determinism. Hmm...

> How I like to think about idempotence is that we have some desired state (the input), and then we have an actual state and the purpose of the program is to bring the actual state to the desired state.

This is a lovely way to look at it!


Idempotence is usually used when talking about effectful code. Modeling the state as part of the input/output seems to run counter to that purpose, because that's how you create a pure model of an effectful program. It's useful to talk about idempotence when running the function zero times is different than running it once, which is hard to do in that framing. As you say, "on the same input" is difficult to reconcile if the state is part of the input.

If we consider the state to be part of the input, then it's easier to say that when f(x) = y, then f(y) = y as well. In other words, f(f(x)) = f(x). Which, as it just so happens, is literally the mathematical definition of idempotence which the programming concept is based on.

In any case, this still gets to the same overall conclusion: the point is to arrive at a specific destination, not to move towards it. That shift of perspective is helpful in framing problems towards an idempotent solution.


> Idempotence is usually used when talking about effectful code.

Agreed! The idempotent operation acts on some state; how that state is modeled is an independent concern.

> In other words, f(f(x)) = f(x). Which, as it just so happens, is literally the mathematical definition of idempotence which the programming concept is based on.

That's the one I like the most, because I try to concentrate impurity to a small region of the overall program. In a language like Haskell, in some ways your hand is forced! But, as you say, it's biased toward a particular way of modeling state and effects.

My point is, if a program is said to be idempotent if running it multiple times on the same input produces the same output, then you exclude the Haskell approach to idempotence. Half the point of `f(f(x)) = f(x)` is that the input f(x) is typically not the same input as x itself!


> This is a lovely way to look at it!

I wish I had thought of that example too when I wrote about idempotency!

I wrote a post about idempotence as well a couple months back (https://chrisprijic.com/idempotency-is-key/) and use the definition: "its the output + side-effects that should remain the same, regardless of how many times the function with the same input is run".

I had struggled with "on the same input" when I was putting the post together until I thought about how it usually is due to side-effects that idempotency becomes desirable.


It's not really uncomfortably close to determinism, it is baseline deterministic which is necessary for the verification. The powerful part of idempotence is that it's deterministic + a verified assurance "as if we run it only once."

Deterministic (but not idempotent): send_message(id=1, body) without verifying the state of id, could result in the message being received multiple times.

Idempotent: send_message(id=1, body) would verify that the id token hadn't been consumed, before acting, thus only allowing the message to be sent one time.


In your second example, if you run `send_message(id=1, body)` once and then run `send_message(id=1, body)` again, it behaves differently the second time than the first time, despite receiving the same input. So `send_message` is idempotent, but its behavior is not determined by its inputs (i.e. it is not pure).

If you imagine the hidden state that `send_message` reads and writes to be part of the input, then when you run it the second time, its inputs have changed. So it wouldn't be correct to say you're running it multiple times "on the same input".

These details are not obvious from the given definition, and that's why I say it's uncomfortably close to purity. Idempotence is very much not about purity (though you can have both), and that's why I like the author's goal-oriented explanation better: it makes the role of state very explicit.


Yeah, my example was bad. I was more trying to point out that determinism of the intrinsic action is a pre-condition to the idempotency, in order to make and verify the assurance required.

You could easily split the deterministic action, and comparison/verification against existing state into two different Pure functions. Being able to perform the verification separately is valuable for addressing the "at most once", "at least once", and "exactly once" assurance described in another comment in this the thread.

I was casually opening my scope up to the outer state, in the example, to express the "assurance" portion of the definition. But you're correct, it's an erroneous example that doesn't pass the more rigorous definition of Pure Determinism, when applied the local scope of the function.


Idempotence is a very useful property to have in a distributed system. This is because most distributed systems are (by definition) not atomic wrt state transitions. For example, it is not cheap to guarantee exactly once delivery over an event bus. If you can build in idempotence, your gonna have a much easier time adapting to changing business requirements


What program was used for the diagrams?


OP here. I draw them on a tablet. For more, see https://marcel.is/drawings-first-steps/


Thank you, that's amazing, I would love to get to this level!


FYI I cannot reach the URL linked.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: