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

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Stack Overflow Blog
Stack Overflow Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
The Exploit Database - CXSecurity.com
美团技术团队
P
Proofpoint News Feed
S
Schneier on Security
P
Privacy International News Feed
Last Week in AI
Last Week in AI
Scott Helme
Scott Helme
V
Vulnerabilities – Threatpost
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tor Project blog
S
SegmentFault 最新的问题
Security Latest
Security Latest
月光博客
月光博客
A
About on SuperTechFans
Martin Fowler
Martin Fowler
A
Arctic Wolf
C
Cyber Attacks, Cyber Crime and Cyber Security
腾讯CDC
Schneier on Security
Schneier on Security
H
Hacker News: Front Page
TaoSecurity Blog
TaoSecurity Blog
NISL@THU
NISL@THU
B
Blog RSS Feed
C
Cybersecurity and Infrastructure Security Agency CISA
Cyberwarzone
Cyberwarzone
V2EX - 技术
V2EX - 技术
Hacker News - Newest:
Hacker News - Newest: "LLM"
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
W
WeLiveSecurity
Cisco Talos Blog
Cisco Talos Blog
MyScale Blog
MyScale Blog
M
MIT News - Artificial intelligence
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
P
Proofpoint News Feed
F
Fortinet All Blogs
aimingoo的专栏
aimingoo的专栏
N
News and Events Feed by Topic
The Last Watchdog
The Last Watchdog
Engineering at Meta
Engineering at Meta
博客园 - 叶小钗
T
Tenable Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LINUX DO - 热门话题
Help Net Security
Help Net Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
G
Google Developers Blog

blag

SQLite prefixes its temp files with `etilqs_` - blag Setsum - order agnostic, additive, subtractive checksum - 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 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
Galloping Search - blag
2024-12-07 · via blag

I recently learned about an algorithm called Galloping Search. It’s used to search sorted items when the upper bound is unknown. It’s like binary search but without the ‘high’ value. In this short post, I’ll explain my problem and how I solved it.

I am building a distributed log over S3. In a bucket or directory, I continuously add files named with sequential integers:

s3 bucket

The writer keeps a counter in memory. On each insert request, the writer increments the counter, assigning a unique sequential number to the new object. There are no gaps. If the machine crashes, I need a way to locate the last inserted object—the one with the highest number.

S3’s limitations make this challenging:

  • S3 has no API to fetch the last inserted item.
  • The LIST API doesn’t support sorting; it always returns results in lexicographical order.
  • I don’t want to scan the entire bucket of hundreds of thousands of items because the S3 LIST API is expensive (it costs the same as a PUT!).

Solution

Here’s what I came up with: I search for objects at exponential intervals (1,000th, 10k, 50k, 100k) in parallel. When I find a gap (e.g., 100k missing but 50k exists), I binary search that range (e.g., 60k, 75k, 90k) until I narrow it to a manageable gap (5–10k objects). Then I use S3’s LIST API to fetch objects from that point.

galloping search

Turns out this is called Exponential Search (or Galloping Search):

Exponential search allows for searching through a sorted, unbounded list for a specified input value (the search “key”). The algorithm consists of two stages. The first stage determines a range in which the search key would reside if it were in the list. In the second stage, a binary search is performed on this range.

When I posted this online, there were lots of questions, and many people offered alternative solutions to find the largest number:

  • The most common (and boring) answer was to keep a counter in a local file or SQLite database. This doesn’t work because it’s not helpful if my machine crashes and I need to recover from S3.
  • Use DynamoDB, Redis, or another database: This works but also kinda sucks because I don’t want to add another dependency to my library. It’s better if everything is self-contained in S3.
  • Store a counter in S3 and update it at every write: This adds write amplification, and S3 PUT costs are expensive. I’d essentially pay twice for each write!
  • Use ULID/UUID/Timestamps: This doesn’t work because I would lose point lookups. I want numbers to be sequential.

Alternate Solutions

  • Inverse Sequencing: Store a counter starting from the maximum value (u64::max) and decrement it with each insert. The S3 LIST API is lexicographical, so you always get the last inserted filename with a single list call. I am split on this solution as I find it cognitively taxing.

  • Partitioning and Hierarchical Search: Store objects with partitioning using a delimiter like 000/042/001. When you partition, the LIST API returns only the top hierarchy results (if you pass the delimiter / in the search request). For 100,000,000 (100M) files, it only takes 3 requests to find the largest number since the LIST API can return up to 1,000 items. For comparison, it is 50+ calls even when I use Galloping Search.

What I’m Doing

  1. At every 10k or 50k writes, I write the current count in a file called .metadata. This reduces both cost and write amplification. I call this the checkpointing operation.
  2. While searching, I start from the counter in the .metadata file. Then I perform the Galloping Search. Even if the metadata file doesn’t exist, the approach still works.

After checkpointing, gaps may exist in the files preceding the last checkpointed number, but this does not impact its effectiveness. For now, I’m happy with the approach, though I may move to checkpointing + partitioning in the future.


1. Thanks to folks @0xriggler and @JustinWaugh on X (formerly known as Twitter) for telling me about partitioning search.
2. The s3-log project is open source.
3. I use S3’s conditional write to “append” and add a new object with the next sequence number.
4. Having gaps in the log can be catastrophic. A writer may add a new object at the gap and return success to the client 💀