18 ms·
Caching is an abstraction, not an optimization
- ckdot2 1y ago"I think now caching is probably best understood as a tool for making software simpler" - that's cute. Caching might be beneficial for many cases, but if it doesn't do one thing then this is simplifying software. There's that famous quote "There are only two hard things in Computer Science: cache invalidation and naming things.", and, sure, it's a bit ironical, but there's some truth in there.
- bell-cot 1y ago(You forgot off-by-1 errors.) All software has to name things, and count. Caching (including invalidation) is best understood as a liability. If you can foist it off on your CPU and OS and DB, good for you. Programming whatever you're actually trying to get done is already hard enough.
- yxhuvud 1y agoOff by 1-errors is not part of the original quote, but is just a later addon to make it funny. They also tend not to be very hard.
- TeMPOraL 1y agoExcept when they're part of some base assumptions in the domain or dozen of layers of abstractions below you. They are hard to prevent from happening.
- tombert 1y agoThey're not hard but I will say that when I was writing an app that was using both JavaScript and Julia, I kept getting off-by-one errors because Julia starts at 1 instead of 0. Really the only time in my entire professional career that off-by-one errors have actually given me headaches.
- bobthepanda 1y agoI think that is from a time when popular programming languages varied in their behavior and also when people were writing for loops with incrementation all the time. A lot of languages have just settled on zero indexing, and many now have some variation of for/each or for/of that would eliminate a lot of potential ways to encounter this error.
- tombert 1y agoYeah, I mostly will do map/reduce/filter when possible, and obviously in those cases indexes don’t matter. It could start at index 12345 for all I care with that stuff. Occasionally, though, I need to use the same index across multiple items, there’s not a trivial means in which to zip, and at that point I have to use an old school for loop. That’s when the 1-index vs 0-index bites me.
- Cthulhu_ 1y agoIf you omit the off-by-1 error from the two hard things joke, you're still off by 1, right? Kind of?
- whateveracct 1y agocaching often does simplify software though when done well and - as the OP suggests - it works best when the cache is a well-defined abstraction with properties and rules about how it works just because "caching" is mentioned in a meme doesn't mean it can't be true that it can simplify software
- BowBun 1y ago> caching often does simplify software though when done well I have to push back here, I think this is objectively untrue. By definition a system or piece of code on where you add a condition where something else happens (cache) that behaves differently than the uncached path increases complexity. I'm not saying it's wrong to cache things or that they aren't useful, but I think they absolutely are an abstraction and an optimization at the cost of complexity. Good code bases hide complexity from the devs all the time, so it's not a question of whether you can code it away, but rather how difficult is it to troubleshoot the internals of the system.
- PaulHoule 1y agoTrying some other way to explicitly manage multiple storage tiers could get pretty complicated.
- jameshart 1y agoIf you hide caching away as an implementation detail behind an abstraction, it comes back and bites you as a leaky abstraction later. Look at how CPU cache line behaviors radically change the performance of superficially similar algorithms. Look at how query performance for a database server drops off a cliff the moment the working cache no longer fits in memory. Hiding complexity can be a simplification, until you exceed the bounds of the simplification and the complexity you hid demands your attention anyway.
- atq2119 1y agoCPUs are still a great example for how caching simplifies things. There's a long history in computer architecture of cores and accelerators that don't have a cache but instead rely on explicitly programmed local scratchpads. They are universally more difficult to program than general purpose CPUs because of that.
- EGreg 1y agoI never understood about cache invalidation or naming things Both are not that difficult, honestly. Aren’t there a lot harder things out there
- szundi 1y ago[dead]
- gryfft 1y agoIt's a little bit tongue in cheek; no one is seriously suggesting it's harder than P=NP or the problem of consciousness. But there's something a bit "death and taxes" to the inevitability that any large enough project is going to have some corner cases involving these old chestnuts. Heck you can probably prove that any system for naming things is either inconsistent or incomplete.
- TeMPOraL 1y ago> no one is seriously suggesting it's harder than P=NP or the problem of consciousness. Well, I for one feel that "naming things" ultimately boils down to the latter, which may or may not be harder than the former.
- Valodim 1y agoIn my experience, the larger the software you write, the truer these become. At some point all obvious names will have collisions, and getting caching right is crucial to do but difficult to achieve because it transcends the entire stack. You could group these two things into "getting the data model right" as the single hard thing, perhaps that rings more true to you :)
- quuxplusone 1y agoFor "only two hard problems," read "two candidates for among the hardest problems (but we feel strongly that these are indeed good candidates)," or something along those lines, more or less. It's also possible that these used to be the only two hard problems at the time the aphorism was first recorded, but the underlying state of the world has changed since then and the aphorism, as recorded, is no longer current.
- bloppe 1y ago"Two programs could have similar behaviour but structured very differently, the difference being that one utilizes caching as an abstraction and one explicitly has the concept of different tiers of storage." The author is comparing "off-the-shelf" caching with custom caching. They're coming from the assumption that you must be caching somehow and arguing that the word "caching" should be understood to mean only particular approaches to the general idea of caching. And obviously the whole point of the general idea is to optimize things. It's a rhetorical mess
- heikkilevanto 1y agoCaching is simple, yes. The hard part is in the last word, invalidation. Even that is manageable for a single process. But as soon as you have multiple (threads / processes / nodes / data centers) updating the data, it does get quite complex, pretty fast. Likewise, naming things is simple as long as you alone, or a in a small team. But as soon as there are multiple organizations with all their own traditions, it gets tricky. Just witness the eternal flame wars about camelCase, PascalCase, snake_case, kebab-case, and UPPER_CASE. It is almost as hopeless culture clash as Emacs vs Vi vs PowerPoint... (I leave the off-by-one errors as an exercise for the reader)
- TeMPOraL 1y agoI'd say this is not the "naming things" that's hard. Beyond picking a common identifier format in the team, there are at least two dimensions that are much harder: - The language dimension - choice of words, that are good enough for the purpose, and not confusing. For example, "Manager" is as ambiguous as it gets, it can mean many thing, except we've been using it long enough that there's a more specific shape of meaning[0] for that word in code/program architecture contexts - so you still would use it instead of, say "Coordinator", which would raise all kinds of questions that "Manager" no longer does. - The epistemological dimension - whether the word you chose correctly names the concept you meant, and whether the concept you meant is actually the right one to describe the thing you're trying to describe. Ultimately, this is the hard thing at the root of philosophy. In practice, it manifests like e.g. choice between digging into some obscure branches of mathematics to correctly name the thing "endofunctor" or something, or calling it "Square" and saying "fuck it, we'll clarify the exceptions in the comments". -- [0] - I mean "more specific" in the sense it's distinct from the other meanings and somewhat narrow - but still it's fuzzy as heck and you can't describe it fully in words; it's basically tacit knowledge.
- Xss3 1y agoI try to name things descriptively in simple terms and often end up with NamesAboutThisLong, once they get too long i know the thing is doing too much and some refactoring is needed for readability. I also avoid letting the reader make assumptions. HasPlayerJumpedRecently is bad. What does recently mean? HasPlayerJumpedInLastTenMs is better, even if it's a bit long...Which highlights that it should probably be refactored into a more flexible value; MsSincePlayerLastJumped. If you arent assuming a time var wth Ms is milliseconds you aren't doing games dev so that one slides with me.
- Traubenfuchs 1y agoI never understood this meme. We use caching a lot, anything that gets cached can only be written by one service each. The writing services emit cache invalidation messages via SNS that cache users must listen to via SQS, to clear/update their cache. Alternatively we cache stuff with just a TTL, when immediate cache invalidation is not important. Where‘s the struggle?
- porridgeraisin 1y agoHere's one: everybody invalidating and refreshing their cache at the same time can cause a thundering herd problem.
- hmottestad 1y agoDoes SQS guarantee delivery to all clients? If it does then that’s doing a lot of heavy lifting for you. If it doesn’t guarantee delivery, then I believe you will at some point have a client that reads a cached value thinking it’s still valid because the invalidation message got lost in the network.
- maccard 1y agoEventually. The problem is that eventually delivering that message will result in clients assuming that it will always be the same, when it’s not.
- williamdclt 1y agoYou don’t support read-your-own-write and your cache data might be stale for arbitrarily long. These relaxed consistency constraints make caching a lot easier. If that’s acceptable to your use cases then you’re in a great place! If not… well, at scale you often need to find a way for it to be acceptable anyway
- pton_xd 1y ago> Where‘s the struggle? If there are no real consequences for reading stale data, and your writes are infrequent enough, then indeed you're lucky and have a relatively simple problem.
- deleted 1y ago[deleted]
- hatthew 1y agoIf you have a system with "slow storage", caching is a way to optimize that to "storage that is sometimes fast". If you have a system with "slow storage" and "fast storage", caching is a way to abstract that away to just "storage". The author is arguing that the latter is the default way we should think about the concept of caching, which is a valid opinion to have.
- deleted 1y ago[deleted]
- AdieuToLogic 1y ago> There's that famous quote "There are only two hard things in Computer Science: cache invalidation and naming things.", and, sure, it's a bit ironical, but there's some truth in there. The joke form of this quote goes along the lines of: There are only two hard things in Computer Science: cache invalidation, naming things, and off-by-one errors. :-D
- dcminter 1y agoI rather like the snark of: there's two hard problems in computer science: we only have one joke and it's not funny. Apparently⁰ by Philip Scott Bowden¹ ⁰ https://martinfowler.com/bliki/TwoHardThings.html https://martinfowler.com/bliki/TwoHardThings.html ¹ https://x.com/pbowden/status/468855097879830528 https://x.com/pbowden/status/468855097879830528
- aorth 1y agoJust remembered another one: there are 10 types of people in the world: those who understand binary and those who don't. :)
- AndrewOMartin 1y agoWhich leads to > I don't see what's so hard about DNS, it's just cache invalidation and naming things.
- SAI_Peregrinus 1y agoMy favorite variation only really works in text: There are three hard problems in Computer Science: 1) Cache invalidation 2) Naming th3) Concurings rency 4) Off-by-one errors
- deleted 1y ago[deleted]
- Joker_vD 1y agoThere is also an important (but often overlooked) detail that you/your application may not be the only user of the cache. At which point caching, indeed, is an optimization via abstraction: when you fetch an X, you are in no position to predict that the next fifty completely unrelated to you requests would also want to fetch the same X, so it should probably be cached to be readily served. Which is why solving the "I want my data in fast storage as often as possible" problem may be counter-productive on the whole: you ain't the only client of the system; let it breath and server requests from others.
- eigenform 1y agoEven more obvious if you think about the case of hardware-managed caches! The ISA typically exposes some simple cache control instructions (and I guess non-temporal loads/stores?), but apart from that, the actual choice of storage location is abstracted away from you (and your compiler).
- necovek 1y agoOn top of the other things mentioned (caching always introduces complexity with lifetime tracking, and thus can't make things simple), the article's got it the wrong way around. When code has abstract interfaces for data access, introducing caching can be simpler (but not simple) by localizing it in the abstraction implementation which has or doesn't have caching. But it is not an abstraction (you can perfectly well do caching without any abstractions, and it's frequently done exactly that way).
- movpasd 1y agoI think you and the article are referring to abstractions over different concerns. The concern you're talking about is about the actual access to the data. My understanding of the article is that it's about how caching algorithms can abstract the concern of minimising retrieval cost. So in some ways you're coming at it from opposite directions. You're talking about a prior of "disk by default" and saying that a good abstraction lets you insert cache layers above that, whereas for the author the base case is "manually managing the layers of storage".
- foldU 1y agoThis is correct, I appreciate you for putting it so coherently :). I think I didn’t make it clear enough in the piece that I’m coming from a stance of fast access being table stakes, and the question being about how that’s accomplished.
- necovek 1y ago"Caching" is an idea of storing a result of an expensive computation in storage that is faster to get from than doing the original computation (in very generic computer terms, computation can be simply fetching from the network or slower local storage). What you describe as "caching algorithms" are not really caching algorithms, but cached object lifetime management algorithms (LRU, LFU...). "Abstraction" is a higher level, simplified view of a set of concepts, yet caching is a single concept. See eg. https://en.wikipedia.org/wiki/Abstraction_(computer_science) https://en.wikipedia.org/wiki/Abstraction_(computer_science) It sounds like you are both trying to redefine what "caching" means (tying it to implementations of particular algorithms), but also what "abstraction" means. We should be very deliberate with the language we use, and our main goal should be to make it simpler to understand, not harder — I believe you are doing the latter here.
- gmuslera 1y ago"fast storage" is about performance, your abstraction includes performance elements. If you go that down, then you are optimizing on your abstraction designs. What doesn't have to be wrong, but then don't say that is not optimization.
- LudwigNagasena 1y agoCaching is an optimisation. Sometimes caching can be abstracted away, eg CPU cache or build cache are pretty much abstracted away for a usual web developer. But web page caching is very hard to abstract without any abstraction leaks and weird bugs. And even CPU cache is no longer an abstraction if you deal with very high performance code.
- gblargg 1y agoIt sounds like they are arguing that when performance matters, you have to know more about caching. Fair enough, you have to know a lot more about things when optimizing. For a lot of cases you can ignore caching because it can be done transparently. You depend on it to some extent because if e.g. every instruction had to be fetched off rotating storage like the old days, it would play a big role in your design. It's just something solved in general for most software to not have to know much about it.
- deleted 1y ago[deleted]
- k__ 1y agoAnything can be an abstraction if designed carefully.
- jbverschoor 1y agoEverything is caching. Almost nothing operates on the target data directly.
- necovek 1y agoDo you think that's a useful definition of the term? If everything is caching, why even introduce the term: language should help us describe ideas, it should not be superfluous.
- jbverschoor 1y agoBecause you can operate directly on data
- canyp 1y agoDid you hand-draw that graffiti? Never quite realized that graffiti of technical ideas looks really goated. Best part of the post, to be honest.
- jxjnskkzxxhx 1y ago> looks really goated Oof you're trying so hard you could cut diamond with that line.
- canyp 1y agoI don't even understand what that means. Care to explain?
- the__alchemist 1y agoI think it's a drug reference‽
- timewizard 1y ago> I've always been told that caching is a tool to make software faster. Who told you that? > you don't have to go all the way back to some backend database or API server or SSD [...] Caching is thus a tool to improve performance. That's called "latency." This is not at all the same as "performance." > My feelings now are that that perspective on caching is wrong I agree.
- taeric 1y agoThis reminds me of the use of materialized views as both a cache strategy and as an abstraction helper.
- bravesoul2 1y agoAnd they too can slow things down. Like all caches can. Like Redis can. Cache is a leaky abstraction. (Although a materialised view is more like an index than a cache. The view won't expire requiring you to rebuild.)
- necovek 1y agoI believe this same language use is what makes this article confusing: Redis is not a cache, it is a key value store. Caching is usually implemented using key value stores, but it is not an abstraction (leaky or not). In RDBMS contexts, index really is a caching mechanism (a cache) managed by the database system (query planner needs to decide when it's best to use one index or another). But as you note yourself even in these cases where you've got cache management bundled with the database, having too many can slow down (even deadlock) writes so much as the database tries to ensure consistency between these redundant data storage elements.
- bravesoul2 1y agoI thought Redis grew up as a KV cache and persistent storage came later. In some sense though. If it ain't L1 it's storage :)
- deleted 1y ago[deleted]
- necovek 1y agoMaybe Redis started up as an in-memory KV store focused on caching use cases, but it was still a KV store that could be used for caching, or not. Even if you use "cache" in the name (eg. memcached), that's still not a cache, even if it's a KV store designed for caching.
- neuroelectron 1y agoThis is basically semantic argument, and I will not be engaging in it
- jxjnskkzxxhx 1y agoYou're right, but caching is an optimization.
- pclmulqdq 1y agoUse of a better abstraction is an optimization, though.
- jongjong 1y agoI was discussing this with someone recently, caching is one of those things that people might do behind the scenes, thinking that it doesn't affect the API but in fact it can create all sorts of issues/complexity.
- zmj 1y agoThis article is talking about single-writer, single-reader storage. I think it's correct in that context. Most of the hairy problems with caches don't come up until you're multi-writer, multi-reader.
- TristanDaCunha 1y agoThis whole discussion on caching and abstraction was completely befuddling to me.
- klabb3 1y agoNote: the author means that caching can be used as an implementation detail in an (abstracted) storage access system, as opposed to a baseline of having multiple storage systems (fast, medium, slow) and managing them directly. This was confusing to me – the most obvious way to judge the purpose of a system is to compare with the baseline of not having that system at all, especially in the case of caching where the program is functionally complete and correct without a cache. Anyway, there may not be a right or wrong here. Just tripped me up.
- yetanotherjosh 1y agoYes "good" caching - a consistent storage interface - is an abstraction over "bad" caching - multiple different storage interfaces with different speeds. But caching overall is not an abstraction over not having caching.
- armchairhacker 1y agoMost optimizations require you to think about how your code is structured, so as a side-effect you make the code more understandable. In this article, it's cache levels forcing you to separate different types of data because they're accessed at different frequencies. Another example is Rust's borrow checker, whose main purpose is arguably to facilitate both safe and efficient memory management, but which can also be used to enforce invariants that aren't clearly memory-related (e.g. builder pattern, temp files that auto-delete after they're dropped). These aren't abstractions though. An abstraction is the opposite, hiding structure when it's noisy and making it easier to change. For example, if you already have an architecture in mind and don't want to manually determine how frequently each type of data is accessed, it's better to use a compiler or library that automatically determines what to cache with little to no code or thought on your end; that's abstraction. Similarly, the abstract analogue to Rust's borrow checker is garbage collection, which allows programmers to not think about their data-structures' lifetimes at all. The cost is usually performance and you understand your application less in some ways (although you understand it more in other ways; abstraction hides details but too many details make it hard to see the big picture. Ideally, with abstractions in the right places, you hide only the "unimportant" details in ways that insignificantly affect performance).
- charleshn 1y agoAs can be seen from other comments, people tend to focus on the consistency implications, but something not discussed often in the context of distributed systems is that caches tend to introduce bimodality and metastability [0] [1]. See e.g. DynamoDB for an example of design taking it into account [2]. [0] https://brooker.co.za/blog/2021/08/27/caches.html https://brooker.co.za/blog/2021/08/27/caches.html [1] https://sigops.org/s/conferences/hotos/2021/papers/hotos21-s11-bronson.pdf https://sigops.org/s/conferences/hotos/2021/papers/hotos21-s... [2] https://brooker.co.za/blog/2022/07/12/dynamodb.html https://brooker.co.za/blog/2022/07/12/dynamodb.html
- 0xbadcafebee 1y agoSometimes posts are so difficult to read they're hard to respond to. I think I get what they're saying. I think they're saying that they think caching should be simple, or at least, that it should be obvious how you should cache in your particular situation such that you don't need things like algorithms. But that argument is kind of nonsense, because really everything in software is an algorithm. Caching is storing a copy of data in a place or way that it is faster to retrieve than it would be otherwise. Caching is not an abstraction; it is a computer science technique to achieve improved performance. Caching does not make software simpler. In fact, it always, by necessity, makes software more complex. For example, there are: - Routines to look up data in a fast storage medium - Routines to retrieve data from a slow storage medium and store them in a fast storage medium - Routines to remove the cache if an expiration is reached - Routines to remove cache entries if we run out of cache storage - Routines to remove the oldest unused cache entry - Routines to remove the newest cache entry - Routines to store the age of each cache entry access - Routines to remove cache entries which have been used the least - Routines to remove specific cache entries regardless of age - Routines to store data in the cache at the same time as slow storage - Routines to store data in cache and only write to slow storage occasionally - Routines to clear out the data and get it again on-demand/as necessary - Routines to inform other systems about the state of your cache - ...and many, many more Each routine involves a calculation that determines whether the cache will be beneficial. A hit or miss can lead to operations which may add or remove latency, may or may not run into consistency problems, may or may not require remediation. The cache may need to be warmed up, or it may be fine starting cold. Clearing the cache (ex. restarts) may cause such a drastic cascading failure that the system cannot be started again. And there is often a large amount of statistics and analysis needed to optimize a caching strategy. These are just a few of the considerations of caching. Caching is famously one of the hardest problems in computer science. How caching is implemented, and what it affects, can be very complex, and needs to be considered carefully. If you try to abstract it away, it usually leads to problems. Though if you don't try to abstract it away, it also leads to problems. Because of all of that, abstracting caching away into "general storage engine" is simply impossible in many cases. Caching also isn't just having data in fast storage. Caching is cheating. You want to provide your data faster than actually works with your normal data storage (or transfer mechanism, etc). So you cheat, by copying it somewhere faster. And you cheat again, by trying to figure out how to look it up fast. And cheat again, by trying to figure out how to deal with its state being ultimately separate from the state of the "real" data in storage. Basically caching is us trying to be really clever and work around our inherent limitations. But often we're not as smart as we think we are, and our clever cheat can bite us. So my advice is to design your system to work well without caching. You will thank yourself later, when you finally are dealing with the bug bites, and realize you dodged a bullet before.
- dasil003 1y agoWhat? No, caching means a specific thing: keeping a copy of data away from the source of truth, closer to where you want to read it. Caching always makes systems more complex, it never makes things simpler, and it damn sure doesn't serve as any kind of abstraction unless you're redefining what words mean to indulge your technical philosophizing.
- hansvm 1y agoWhat if you have to keep some data closer and away from the source of truth though? Given that constraint, TFA argued that other architectures could do the job but that caching functions as an abstraction.
- scrubs 1y agoOusterhout's grad students did work on ramcloud with some research at facebook and Amazon on cache use at scale in complex organizations. One bit of interesting trivia say for facebook (from memory): if you add all the RAM caches in redis/memcached/disk + db caches to make the thing work at scale, then for about 20-30% more memory you could've had the whole thing in memory 100% of the time.
- kazinator 1y agoOptimization isn't separable from abstraction. Abstraction is something that can be implemented in more than one way, while meeting the terms of its contract. That flexibility allows for optimization.
- chrisjj 1y agoWhy not both? :)
- suspended_state 1y agoLet's first get the obvious out of the way: caching is not an abstraction, the "Storage" abstraction is what enables caching to be implemented. If I had to put Caching in a category, I would say that it's an optimization strategy. But that's not really what the blogpost is about. The issue that it tries to discuss is the fact that this abstraction is often imposed to us, without any way to control its behaviour. That's the examples of the LOAD_NAME in python he points at. Without having a clear understanding of the access patterns the application mostly uses, a caching strategy cannot be well defined, and you'll end up with an inadequate solution.
- kiitos 1y agoCmd+F "invalidation" -- not found. Author is talking about the least interesting, and easiest, piece of the overall caching problem.
- flufluflufluffy 1y agoI don’t really know what the point is… there are different kinds of caching that serve different purposes, just like everything else..