Ep 107: Idempotify!► Play Episode
Each week, we discuss a different topic about Clojure and functional programming.
This week, the topic is: "handling endless errors". We discover when giving up is the way to get ahead.
Our discussion includes:
- Finishing up our Sportify tracer-bullet implementation.
- What context can you put in a file name?
- Tricks on working with time information.
- Trying to "fix" a situation vs adding more context.
- What is a "positive representation" of a problem?
- What are "fundamental" versus "derived" facts?
- What information tends to be more stable?
- What information should you try to co-locate?
- How do you handle resources that are not currently available?
- How many times should you retry?
- How long should you wait between retries?
- How do you figure that out?
- What is idempotency? How can it help?
- At what point should you stop trying to handle errors?
- Can you have too much automation?
- How can you troubleshoot intermittent problems?
We don't need a time zone offset because we know it's in UTC!
In the spirit of building up the language to meet our domain, we can write a pure function!
We want a deterministic way to go from this kind of common information into all the other bits of derived information.
I was really hoping that we would finally be done with the errors and we could just get a highlight clip, but yet again, the world has conspired against us to make our life difficult as a programmer!
Hold on! Hold on! The first thing we should do is run our process again because maybe the error will just go away!
The problem does not go away in this instance. The problem just goes back to being hidden!
The frustrating thing about programming is that code will do exactly what you told it to do.
Clojure is already positive about nothing, that's what we call
nil, so why not be positive about bad stuff too?
Now I'm personifying the function as myself!
It's better to reference information by its main identifier as opposed to some derived identifier.
The context is all over the place: some context in memory, some context in the file system, and some intermediate context in the imperative function.
That's the last problem we encounter, right? You never know what the world's going to throw at you!
Why don't we just run it again? Let's just run it again! Maybe it'll be ready now... Maybe now... Maybe it will be ready now...
And then we hit another error which is: the MAM rejects us for making too many requests!
How long do you wait? Should you back off? That adds a lot of complexity to the code at that level.
Because adding idempotency increases the complexity of that part of the solution, we only want to add it where it's necessary.
The error condition is happening right now, so let's write the code to fix it right now.
That's the way automation is at some point in time: a human needs to do it.
You can find yourself in a situation where you're trying to do too many automatic things. Is it really worth doing? You cannot solve every possible permutation once you're interacting with the real world.
It's okay to put up the guardrails, and if I'm outside them, robot me throws up my hands and says, "Human please!"
But what happens when you run it again and the clip is ready? Now your retry logic doesn't get tested.