HN user

theanomaly

130 karma

[ my public key: https://keybase.io/tomcallahan; my proof: https://keybase.io/tomcallahan/sigs/e1LpMUePWH56IClGKKuE5AEH_QqRWRo_VOCUNyjTiKM ]

Posts1
Comments14
View on HN

We're running Aurora PostgreSQL 15.12, which includes the fix mentioned in the release notes. Looking at this comment and the AWS documentation, I think there's an important distinction to make about what was actually fixed in Aurora PostgreSQL 15.12.4. Based on our experience and analysis, we believe AWS's fix primarily focused on data protection rather than eliminating the race condition itself.

Here's what we think is happening: Before the fix (pre-15.12.4):

1. Failover starts

2. Both instances accept and process writes simultaneously

3. Failover eventually completes after the writer steps down

4. Result: Potential data consistency issues ???

After the fix (15.12.4+):

1. Failover starts

2. If the old writer doesn't demote before the new writer is promoted, the storage layer now detects this and rejects write requests

3. Both instances restart/crash

4. Failover fails or requires manual intervention

The underlying race condition between writer demotion and reader promotion still exists - AWS just added a safety mechanism at the storage layer to prevent the dangerous scenario of two writers operating simultaneously. They essentially converted a data inconsistency risk into an availability issue. This would explain why we're still seeing failover failures on 15.12 - the race condition wasn't eliminated, just made safer.

The comment in the release notes about "fixed a race condition where an old writer instance may not step down" is somewhat misleading - it's more accurate to say they "mitigated the consequences of the race condition" by having the storage layer reject writes when it detects the problematic state and that is probably why AWS Support did not point us to this release when we raised the issue.

AWS Support initially pushed back and suggested it's because of high replication lag but they were looking at metrics that were more than 24 hours old. What kind of failure did you encounter? I really want to understand what edge case we triggered in their failover process - especially since we could not reproduce it in other regions.

I'm surprised this hasn't come up more often too. When we worked with AWS on this, they confirmed there was nothing unique about our traffic pattern that would trigger this issue. We also didn't run into this race condition in any of our other regions running similar workloads. What's particularly concerning is that this seems to be a fundamental flaw in Aurora's failover mechanism that could theoretically affect anyone doing manual failover.

While it's tough if you want new drives, I've found I could frequently get used drives on eBay that have significant history on Backblaze's report. Despite the increased risk from used drives, I've found I still end up more reliable than buying random new drives.

Right. So let's say we have file A, which is an innocuous image file, and file A', which is a malicious image file, where MD5(A) == MD5(A'). Based on the MD5 prefix collision attack, I should be able to construct two such files A and A'.

I get an edgemesh site to accept file A (perhaps the site allows me to upload a user avatar, upload an image on a forum, etc). I then behave as a node in the mesh, and receive file A. When I get a request to replicate file A to someone else, I send them file A', they check the MD5 hash, and the hash matches. Not seeing how that doesn't work?

It is admittedly a narrow attack, but I think it works.

Ah I see, I forgot that in the SSL attack the attacker had to choose both certificate prefixes as opposed to just one. Thanks!

It does seem to me though that if I could coerce/direct the site into accepting one image that I created, I could manage to replicate a second, different file throughout the network. Obviously assuming I computed both images ahead of time and both image formats were unperturbed by the nonsense appended to file by the attack.

Looking at the image on the link, the "checksums" are a suspicious 32 characters... Hoping you guys are not using md5sums.

Am I missing something, or would this let any node (supernode/browser) in the system potentially replace arbitrary content with their own content? [1]

Hopefully JS isn't being served by this mechanism (attack vector pretty obvious there), but even images are still a concern [2] [3].

[1] https://en.wikipedia.org/wiki/Collision_attack#Chosen-prefix...

[2] https://threatpost.com/apple-patches-ios-flaw-exploitable-by...

[3] https://imagetragick.com/

Thanks for the analysis -- it is good that people have this context in their heads when designing systems. The missing conversation from this article is that some people conflate scalability with performance. They are different, and you absolutely trade one for the other. At large scale you end up getting performance simply from being able to throw more hardware at it, but it takes you quite a while to catch up to where you would have been on a single machine.

This is true not just for computing algorithms, but for developer time/brain space as well. Single-threaded applications are far simpler to understand.

The takeaway shouldn't be "test it on a single laptop first", but rather "will the volume/velocity of data now/in the future absolutely preclude doing this on a single laptop". At my work, we process probably a hundred TB in a few-hour batch processing window at night, Terabytes of which remain in memory for fast access. There is no choice there but to pay the overhead.

I'm confused

It’s simple – here’s how it works: Say a community is built in Year 1. The community’s streets need to be rebuilt every 30 years. In Year 30 a new, identical community is built. Now twice the amount of taxes are coming, and so for time being the property owners only need to pay half the amount. And 30 years later, in Year 60, two new communities are built; as long as the number of properties and property taxes are doubling every 30 years, they can continue to pay half the amount.

Year 1, one community (community A), over the next 30 years is going to pay for 1 community's worth of roads (call it a 30 year loan given on day 1, let's say 1M dollars). So, the cost for community A is 1M dollars for 30 community-years of roads.

Year 30, we add a new community B to the mix. This community needs its own roads, so it needs a loan for 1M dollars it will pay off over the next 30 years. However, community A's roads have worn out. Community A just finished paying off their first loan, so they'll need a new one.

At year 60, we have paid 3M dollars, and gotten 90 "community-years" of roads out of it. This is no different than the equivalent end of the first year with one community, one loan, and 30 "community-years" of roads.

What am i missing from this example?

Yes, but you have to know to exclude their backend. And, that library may be several dependencies deep. Now you're expecting potentially junior developers to have the insight to grep their entire transitive dependency tree, find the nop dep, and exclude it. This kind of silent failure is worse than the alternative.

Whoa. The author recommends including slf4j-nop as an explicit dependency of libraries?! This means that when a developer writes an application and forgets to include an slf4j backend, instead of getting a warning that no backend is configured, that developer will get absolutely no output. This is really bad advice.