惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
小众软件
小众软件
S
SegmentFault 最新的问题
人人都是产品经理
人人都是产品经理
博客园 - 【当耐特】
博客园 - 三生石上(FineUI控件)
C
Check Point Blog
S
Schneier on Security
Microsoft Azure Blog
Microsoft Azure Blog
N
Netflix TechBlog - Medium
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
罗磊的独立博客
有赞技术团队
有赞技术团队
V
V2EX
Y
Y Combinator Blog
博客园 - 叶小钗
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
F
Fortinet All Blogs
W
WeLiveSecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog
The Cloudflare Blog
S
Security @ Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
Hugging Face - Blog
Hugging Face - Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
PCI Perspectives
PCI Perspectives
H
Heimdal Security Blog
Schneier on Security
Schneier on Security
Security Latest
Security Latest
AWS News Blog
AWS News Blog
月光博客
月光博客
Security Archives - TechRepublic
Security Archives - TechRepublic
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
博客园 - Franky
Cisco Talos Blog
Cisco Talos Blog
T
Threat Research - Cisco Blogs
M
MIT News - Artificial intelligence
T
Troy Hunt's Blog
N
News and Events Feed by Topic
Cloudbric
Cloudbric
Scott Helme
Scott Helme
云风的 BLOG
云风的 BLOG
Attack and Defense Labs
Attack and Defense Labs

blag

SQLite prefixes its temp files with `etilqs_` - blag Oldest recorded transaction - blag Replacing a cache service with a database - blag SQLite commits are not durable under default settings - blag PSA: SQLite WAL checksums fail silently and may lose data - blag Rickrolling Turso DB (SQLite rewrite in Rust) - blag Collection of insane and fun facts about SQLite - blag How bloom filters made SQLite 10x faster - blag In search of a faster SQLite - blag Galloping Search - blag Building a distributed log using S3 (under 150 lines of Go) - blag Zero Disk Architecture - blag PSA: Most databases do not do checksums by default - blag PSA: SQLite does not do checksums - blag Disaggregated Storage - a brief introduction - blag Why does SQLite (in production) have such a bad rep? - blag SQLite Slaps - blag Now - blag Learning C - blag Snapshot Testing - blag Win: contribution to libSQL (SQLite) codebase - blag Errata in Hekaton MVCC paper - blag Internet is wholesome: MVCC edition - blag It is becoming difficult for me to be productive in Python - blag MongoDB secondary only index - blag Introducing CaskDB – a project to teach you writing a key-value store - blag Recurse Center: Winter Break - blag Recurse Center Day 24: Hacking Go compiler to add a new keyword - blag Recurse Center Day 20: Django v4 upgrade (from v1) - blag Recurse Center Day 19 - blag Recurse Center Day 18 - blag Recurse Center Day 17 - blag Recurse Center Day 16: Open Source - blag Recurse Center Day 15: B Tree Algorithms - blag Recurse Center Day 14: NoSQL Transactions - blag Recurse Center Day 13: Why 'Raft'? - blag Recurse Center Day 12: Isolation Anomalies - blag Recurse Center Day 11: B Tree Insertions - blag Recurse Center Day 10: Learning Distributed Systems - blag Recurse Center Day 9: Papers We Love - blag Recurse Center Day 8: B Tree Fill Factor (Part 2) - blag Recurse Center Day 7: Basics of ncurses - blag Recurse Center Day 6: B Tree Root - blag Recurse Center First Week - blag Recurse Center Day 5: Garbage Collection Algorithms - blag Recurse Center Day 4: B Tree fill factor - blag Recurse Center Day 3: Hammock Driven Development - blag Recurse Center Day 2: BTree Node - blag Recurse Center Day 1: init - blag What I want to do at Recurse Center - blag Accepted to the Recurse Center! - blag Towards Inserting One Billion Rows in SQLite Under A Minute - blag Marshaling Struct with Special Fields to JSON in Golang - blag I ended up adding duplicate records on a unique index in MongoDB - blag Setting up Github Actions for Hugo - blag Moving to Hugo - blag Catching SIGTERM in Python - blag Git/Github fork-pull request-update cycle - blag Using uWSGI with Python 3 - blag When is my Cake Day? - blag Staying Ahead of Amazon, in Amazon Treasure Hunt Contest - blag How I Am Maintaining Multiple Emails For Git On A Same Machine - blag An exploit on Gaana.com gave me access to their entire User Database - blag Flashing Asus-WRT Merlin by XVortex on NetGear NightHawk R7000 - blag Install Windows 8 UEFI on Legacy BIOS with Clover (and Dual boot with Yosemite) - blag Scraping Javascript page using Python - blag Installing Transmission (remote and CLI) client on Raspberry Pi - blag About - blag Projects - blag
Setsum - order agnostic, additive, subtractive checksum - blag
2025-09-13 · via blag

Setsum is an order agnostic, commutative checksum. It was developed by Robert Escriva at Dropbox’s metadata team. In this short post, I’ll explain why they’re used and the math behind them. Jump to the end if you’d like to see the code.

Introduction

Say you’re building a database replication system. The primary sends logical operations to replicas, which apply them in order:

{"op": "add", "id": "apple"}
{"op": "add", "id": "apple"}
{"op": "remove", "id": "orange"}

After the replica processes these changes (add two apples, remove an orange), how do you verify both nodes ended up in the same state?

One naive (rather horrible) approach is to dump both states and compare them directly. It’s expensive, impractical, and doesn’t scale. Instead, you can maintain checksums that update with each operation. When you’re done, just compare the checksums; if they match, you’re in sync. That’s why distributed databases like Cassandra use Merkle trees for the same purpose.

Setsum is similar but has some nice properties that make it attractive over Merkle trees. They can be computed incrementally; the cost only depends on the change being applied, not the whole dataset. I also find them attractive because they let you remove items as well.

Properties

Setsum has some interesting properties:

1. Order doesn’t matter. Both of these yield the same result:

s1 = New()
s1.add("apple")
s1.add("banana")

s2 = New()
s2.add("banana")
s2.add("apple")

assert_eq(s1, s2)

2. You can remove items. These are equivalent:

s1.add("apple")
s1.add("banana")
s1.remove("apple")

s2.add("banana")
s2.add("banana")
s2.remove("banana")

assert_eq(s1, s2)

3. You can combine setsums. As you guessed already, these are equal:

s1.add("apple")
s1.add("banana")
s2.add("chikoo")

s3.add("banana")
s3.add("chikoo")
s3.add("apple")

assert_eq(s1+s2, s3)

The only state you need to maintain is 256 bits, and all operations are O(len(msg)) instead of depending on your entire dataset.

The internals

Each Setsum is an array of 8 unsigned 32-bit integers (u32), called “columns”. Each column starts at 0 when you create a new Setsum. Each column has an associated large prime number (close to u32::MAX).

When you add an item:

  • Compute the SHA3-256 hash of the item (produces 32 bytes)
  • Split the hash into 8 chunks of 4 bytes each
  • Interpret each chunk as a little-endian u32
  • Add each number to its corresponding column
  • If the sum exceeds the column’s prime, store the remainder (mod prime)

You can also remove an item that was previously added. The magic is in computing the inverse: first, derive the inverse of the item’s hashed value, then add that inverse to the setsum. This effectively cancels out the original, removing the item from the set!

To compute the inverse, we use modular arithmetic: it’s simply the prime minus the value.

The math behind setsum

Disclaimer: If we’re friends, you already know I’m no math person. If not, hey there, new friend! You can probably skip this if you understand modulo arithmetic, the Chinese remainder theorem, and a bit of probability.

Let’s simplify: instead of 8 columns, let’s use just one. The prime number for this column is 29. Consider adding these items with their hash and inverse values:

ItemHashInverse
apple1514
banana236
chikoo722
pomegranate1811
watermelon263

Notice that all hash values stay below the prime. If a hash exceeds it, we take the remainder. For example, if `guava` hashes to 33, the final value would be 4 (33 mod 29). Also, hash + inverse always equals the prime.

Let’s add some items:

s = 0
s = 15 (add apple - 15)
s = 9 (add banana - 23)
s = 16 (add chikoo - 7)

Let’s try in some random order:

s = 0
s = 7 (add chikoo - 7)
s = 22 (add apple - 15)
s = 16 (add banana - 23) // see, this ends up same!

Let’s try removal. Note that for removal we add the inverse values:

s = 0
s = 15 (add apple - 15)
s = 9 (add banana - 23)
s = 27 (add pomegranate - 18)
s = 12 (remove apple - 14)
s = 18 (remove banana - 6)
s = 25 (add chikoo - 7)
s = 0
s = 18 (add pomegranate)
s = 25 (add chikoo) // whoa 🤯

I cherry-picked these examples to demonstrate setsum, but there’s a flaw in the above examples. Can you spot it?

Consider this collision:

s = 0
s = 15  (add apple - 15)
s = 22  (add chikoo - 7)
s = 0
s = 18  (add pomegranate - 18)
s = 22  (add guava - 4)

Both sets of completely different items sum to 22! This happens because we’re only using one column and a very small prime number. But add another column and the collision probability drops dramatically. With 8 columns, the probability of collision drops to 1/2256.

Setsum also uses SHA3-256 as its hash function, though the hash algorithm is replaceable. SHA3-256 is fast, has fewer collisions, and produces well-distributed hashes, so we can avoid the collision problem I showed above.

Observations

  1. Setsum can tell you if states diverged, but not where. To narrow things down, you can split your data into smaller chunks and compare those. Build this into a hierarchical structure and you’re basically back to something like a Merkle tree.

  2. You can remove items that never existed. This might or might not be a problem depending on your use case. Given that you’re only maintaining 256 bits of state, it’s a reasonable tradeoff.

  3. There’s no history tracking. You can’t tell when or how states diverged, just that they did.

Code

The original Rust implementation is here. I ported it to Go, with all the same tests - setsum.


1. I found out about setsum from Chroma’s excellent post wal3: A Write-Ahead Log for Chroma, Built on Object Storage and got nerd sniped into writing this blog post
2. This is a good post: Using Merkle trees to detect inconsistencies in Cassandra

Thanks to toc2, and General Bruh for reading an early draft of this post.