今日已更新 166 条资讯 | 累计 40611 条内容
关于我们

标签:#distributed

找到 87 篇相关文章

AI 资讯

Understanding the Replication Queue in ClickHouse

I was testing out CH-Ops - an admin GUI for self-hosted ClickHouse - on a simple setup: 1 shard, 2 replicas. Stumbled onto the replication queue almost by accident. Here's what I did: I stopped one of the nodes (let's call it Node B), then inserted some data through the other one (Node A). Just wanted to see what would happen. Then, while Node B was still down, I checked it in CH-Ops. It had stuff sitting in its replication queue. My first assumption was: okay, this must be showing what's left to replicate across the cluster - the total pending replication work. So I switched over and checked Node A, the one that was actually up and had just received the insert. Its queue was empty. That didn't match what I expected at all. If the queue was a cluster-wide "here's what still needs to replicate" view, Node A should've shown something too - it was the one that had the fresh data now waiting to reach Node B. Instead it was Node B, the down one, sitting there with pending tasks. That mismatch is what sent me digging. Turns out the queue isn't cluster-wide at all - it's specific to each ClickHouse instance. Once I brought Node B back up, its queue drained in seconds and the data showed up. That whole experiment is basically the entire post in miniature. Here's the mental model I ended up with. A Queue Belongs to a Replica, Not to the Table This is the first thing to get straight. With a ReplicatedMergeTree table, you can have multiple replicas holding copies of the same data. It's tempting to think of replication as one shared pipe between them. It isn't. Each replica keeps its own local replication queue . So if you see: Replica 1 → queue_size = 0 Replica 2 → queue_size = 25 that doesn't mean 25 operations are waiting somewhere in the middle for both replicas to pick up. It means Replica 2, specifically, has 25 tasks it hasn't finished yet. Once that clicked for me, the rest of the system made a lot more sense. So Where Do These Tasks Come From? Replication in ClickHouse

2026-09-08 原文 →
AI 资讯

Batch Processing: From Unix Tools to Distributed Systems

Much of the traditional software operations we deal with are online, we click a button, wait for a moment, and the transaction or operation is completed. But there is a big area that deals with software operations that require offline processing. For example, background processing of jobs, e.g., OpenAI training/improving its existing GPT models behind the scenes using the data it gathers from its users. Batch Processing Whenever such an offline system runs a job that typically generates output from a batch of inputs, we call that batch processing. Inputs here are immutable, which avoids side effects. Benefits of batch processing: You can time travel. In case of any failure or unintentional outputs, you can jump to the last input checkpoint before a batch processing job. This handling is often referred to as human fault tolerance. Using batch processing and offline systems, compute usage efficiency can be improved. For example, whenever a heavy computation needs to be done, it's better to do it in bulk on maybe a GPU compute rather than crashing the CPU host where the server is online. Though the boundary between online and batch processing is not always clear. For example, a long-running database query could also be categorised as batch processing. Another alternative to batch processing is stream processing, which we will understand in the next article. MapReduce MapReduce is a batch processing algorithm that is utilized by Hadoop, CouchDB, and MongoDB as well. It is a balanced approach that is less extreme than completely parallelizing the jobs. There are several other frameworks like this that are now replacing MapReduce. For example, DataFrames APIs, query languages, etc. We will see MapReduce in detail sometime later. Simulating Batch Processing with Unix Tools (Single Host) If you are a Linux user, this simulation could be very easy for you to grasp. If not, just put it in ChatGPT or any AI tool to understand the command in detail if interested. A typical Ngin

2026-09-05 原文 →
AI 资讯

The Transactional Outbox Pattern: Dual-Write Consistency in Distributed Systems

The Transactional Outbox Pattern: Dual-Write Consistency in Distributed Systems One of the most dangerous anti-patterns in microservices architecture is the Dual-Write Vulnerability : updating a database record and immediately publishing an event to a message broker (e.g., RabbitMQ, Kafka) in the same API call. If the network fails or the broker is unavailable after the database transaction commits, the event is lost forever. Conversely, if the event publishes but the database rollback triggers, downstream consumers process a phantom event that does not exist in the source of truth. In this deep dive, we architect the Transactional Outbox Pattern with Change Data Capture (CDC) to guarantee At-Least-Once delivery with zero distributed locking overhead. Technical & Interview Cheat Sheet Approach Consistency Guarantee Failure Mode Overhead Dual Write (Naive) None (Eventual inconsistency) Message lost if broker drops Low 2-Phase Commit (2PC / XA) Strict Atomicity Blocking locks, single point of failure Very High Transactional Outbox (Polling) At-Least-Once Polling query table contention Moderate Outbox + CDC (Debezium) At-Least-Once (Zero Table Locking) Requires WAL decoder plugin Optimal 1: Database Schema Design The business entity change and the outbox event MUST commit within the exact same database transaction: -- Business Entity CREATE TABLE orders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid (), customer_id UUID NOT NULL , total_amount NUMERIC ( 12 , 2 ) NOT NULL , status VARCHAR ( 32 ) NOT NULL , created_at TIMESTAMPTZ NOT NULL DEFAULT NOW () ); -- Transactional Outbox Table CREATE TABLE outbox_events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid (), aggregate_type VARCHAR ( 64 ) NOT NULL , aggregate_id VARCHAR ( 64 ) NOT NULL , event_type VARCHAR ( 64 ) NOT NULL , payload JSONB NOT NULL , created_at TIMESTAMPTZ NOT NULL DEFAULT NOW () ); -- Index for high-throughput CDC streaming CREATE INDEX idx_outbox_created ON outbox_events ( created_at ); 2: Atomic C# Tra

2026-09-05 原文 →
AI 资讯

Airbnb Cuts Authentication Code by 60% with Server Driven Architecture

Airbnb redesigned its authentication architecture around server driven flows and policy based challenge selection. The new Flexible Authentication system reduced authentication related code by 60%, cut the web client bundle by 100 KB, improved successful authentication by 2.6%, reduced duplicate account creation by 27%, and lowered OTP costs by 11%. By Leela Kumili

2026-09-04 原文 →
AI 资讯

CQRS: Read-Write Separation Design Pattern

In traditional software architectures, we almost instinctively reach for the CRUD (Create, Read, Update, Delete) paradigm. We design an entity model, map it to a relational schema using an ORM, and use that identical abstraction to both alter state and display data on user dashboards. For simple applications, this works flawlessly. But as systems scale—both in business complexity and throughput, this dual-purpose model starts showing fractures: Write logic demands tight validation, transactional boundaries, normalization, and domain invariants. Read logic demands flat, pre-aggregated, denormalized representations across dozens of tables to serve responsive UIs. Trying to satisfy both masters with a single schema leads to unwieldy SQL joins, lock contention, compromised domain boundaries, and performance gridlock. This is where Command Query Responsibility Segregation (CQRS) enters the picture. 1. What is CQRS? Coined by Greg Young and based on Bertrand Meyer’s Command-Query Separation (CQS) principle, CQRS states that an application should use separate models to update and read data . At its philosophical core: Command (Write): Represents an intent to alter domain state (e.g., SubmitOrder , DeactivateUser , ChangeBillingAddress ). A command should focus entirely on domain logic, data integrity, and business rules. In strict CQRS, commands do not return domain data — only an acknowledgment, validation failure, or generated entity ID. Query (Read): Retrieves data without mutating application state (e.g., GetOrderSummaryById , ListCustomerInvoices ). Queries should execute side-effect-free operations that return lightweight Data Transfer Objects (DTOs). ┌────────────────────────────────────────────────────────┐ │ Client │ └─────────────┬────────────────────────────▲─────────────┘ │ │ Execute Command Run Query │ │ ▼ │ ┌───────────────────────────┐ ┌───────────┴─────────────┐ │ Command Model │ │ Query Model │ │ (Validation & Invariants) │ │ (Optimized for DTOs) │ └──────

2026-09-02 原文 →
AI 资讯

The wait queue is just a channel: building a small distributed lock server in Go

Sooner or later you hit the same small problem: two services, on two machines, want to touch the same thing at the same moment — append to a shared file, update a row nobody is fencing, call an API that tolerates one caller at a time. One of them has to wait. The usual answers feel heavier than the problem. Put a service in front and serialize everything through it — now you are building a queue, and then a second queue to hand results back, because you no longer know the outcome at the moment you asked. Cache the resource in Redis and lock there — fine until the resource does not fit in memory, and you have inherited Redlock's ordering guarantees (there are none) and its debates. I wanted the lock as its own primitive: lock a key, do the work, unlock the key. Nothing else. That is Locking-Center — a single binary, one dependency, no config file, no consensus layer to operate. This post is about the three ideas that made it small enough to be worth trusting. 1. One channel per key — and the queue comes for free Every key gets a Go channel with a buffer of exactly one: type Channel struct { key string mutexChan chan bool // buffered, capacity 1 } func NewChannel ( key string ) * Channel { return & Channel { key : key , mutexChan : make ( chan bool , 1 )} } Sending into it acquires the lock. Receiving from it releases : c . mutexChan <- true // acquire — blocks if someone already holds the key // ... critical section ... <- c . mutexChan // release The buffer of one is the whole trick. The first send fills the buffer and returns immediately: that caller holds the key. The second send has nowhere to go, so it blocks — and so does the third, and the fourth. The blocked senders are the wait queue. When the holder releases (a receive frees the slot), the runtime wakes the next blocked sender. And it wakes them in order. The Go runtime keeps a FIFO wait queue behind every channel, so callers are served roughly in arrival order rather than whoever happens to reschedule firs

2026-09-01 原文 →
AI 资讯

Your default branch is an allowlist, and it votes healthy

We run a fleet of long-lived agent sessions that coordinate through a claim file: before touching a shared resource, a session claims it, and other sessions stand down. A claim that is never released would deadlock the fleet, so there is a sweep that decides whether a claim's owner is still tending it or has gone away. The sweep's core is a case over an exit code: mesh-mind-state " $win " > /dev/null 2>&1 case " $? " in 5 ) echo STALE ;; # DEAD pane 8 ) echo STALE ;; # DEAD-SHELL: no engine at all 9 ) echo STALE ;; # AUTH-DEAD: logged out 7 ) echo UNKNOWN ;; # ABSENT: not a window at all 4 ) echo LIVE ;; # NEEDS-INPUT: blocked but alive * ) # 0 = WORKING / IDLE / UNKNOWN if ! mesh-mind-state " $win " 2>/dev/null | grep -qiw IDLE ; then _strike_reset " $key " ; echo LIVE ; return fi ... esac Read the *) branch as what it actually is. It does not mean "the state is 0." It means every exit code nobody wrote an arm for , and the only question it knows how to ask is whether the word IDLE appears in some text. Anything that is not the string IDLE is treated as working . That is a classifier whose unhandled input votes healthy. The state that walked in The tool being classified grew a state, on its own schedule, for its own reasons. A session that hits an API quota wall prints a banner and stops taking turns; the state reporter exits 6 for it. # Exit (single window): 0 WORKING/IDLE/UNKNOWN · 4 NEEDS-INPUT · 5 DEAD · 6 RATE-LIMITED # 7 ABSENT · 8 DEAD-SHELL · 9 AUTH-DEAD There was no 6) arm. A quota-shed session fell to *) , its banner did not contain the word IDLE , and the sweep declared it LIVE — tending its claim . Nothing crashed. No log line said anything was wrong. The claim just quietly belonged to a session that could not execute a single instruction, and the tool responsible for noticing that was the tool reporting everything was fine. The bill: one claim sat there reading as merely expired for over five hours , while a nagging reflex kept sending its owner remind

2026-08-31 原文 →
AI 资讯

How BitTorrent Turned Every Downloader Into a Server

Hello, I'm Maneshwar. I'm building git-lrc, a Micro AI code reviewer that runs on every commit. It is free and source-available on Github. Star git-lrc to help devs discover the project. Do give it a try and share your feedback. A couple of posts back we spent a while inside XOR distance , then used it to build Kademlia , the DHT algorithm that lets a network find anything without a directory. Kademlia: Algo That Turned XOR Distance Into a Network Athreya aka Maneshwar Athreya aka Maneshwar Athreya aka Maneshwar Follow Aug 26 Kademlia: Algo That Turned XOR Distance Into a Network # webdev # programming # beginners # algorithms 20 reactions Add Comment 6 min read I promised that algorithm shows up "under BitTorrent, IPFS, Ethereum." Today we cash that check. We're taking BitTorrent apart, piece by piece, and Kademlia is going to walk right back in through the side door. Also, fun fact before we start: a suspicious number of people on Reddit think Bram Cohen, the guy who wrote BitTorrent alone in Python in 2001, is secretly Satoshi Nakamoto. I'm not saying it's true. I'm saying that by the end of this post you'll understand why people keep saying it. The number that should not have been possible In 2004, a measurement firm called CacheLogic reported that BitTorrent alone was responsible for roughly 35% of all internet traffic. More than every other peer to peer network combined. More than the entire web. One protocol. Written by one guy. No company. No datacenter. No servers anywhere with "BitTorrent Inc" on the rack. That last part is the whole story. Every "normal" system you've ever worked on scales by throwing money at it: bigger box, more replicas, a CDN in front. BitTorrent had nobody to throw money at anything, so every hard problem, capacity, trust, scheduling, incentives, discovery, had to get solved inside the protocol itself . Problem 1: the client-server ceiling has a name Distributing a file in 2001 meant one server, one uplink, and every download eating

2026-08-28 原文 →
AI 资讯

Beyond Embedded: How DuckDB v2.0 Shifts Architecture Toward Distributed Network Capabilities

DuckDB Labs has previewed DuckDB v2.0, codenamed "Cyanoptera." This release includes over 10000 commits and introduces a client/server mode, enabling network connections. Improvements also encompass extension portability, advanced data types, and a new parser. Performance enhancements include asynchronous I/O and storage optimisations. General availability is expected in fall 2026. By Olimpiu Pop

2026-08-25 原文 →
AI 资讯

How the Nostr journey is going for me?

A few years ago, I discovered the Nostr protocol 1 thanks to Jack (Jack Dorsey) 2 . I've been using it actively ever since. I already had an interest in distributed social networks for years. I tried ActivityPub 3 for a while, but something just didn't click. Around that time, Jack started ATProto 4 within Twitter, but something felt off there too. I hadn't even bothered trying the methods that came before those anyway. It always bothered me—and many others, of course—that the internet became so centralized when the web is distributed by nature. The main reason for this was social media platforms. The trend that started with guestbooks evolved into forums, then to Friendfeed-like walls, MySpace-like platforms, and eventually Facebook and Twitter. The internet, which had a much more distributed structure with blogs prior to Facebook and Twitter, suddenly started to centralize. Due to the maintenance costs of distributed structures and unsettled systems, humanity took the easy way out and chose centralization, as always. Facebook and Twitter grew steadily, and many other platforms alongside them transformed into centralized hubs using the exact same playbook. One of the things people realized too late was that centralized power always possesses the ability to manipulate, a fact that eventually came to light through the Facebook and Twitter lawsuits and similar cases over time. Moreover, there was no such thing as account ownership in any of these centralized systems; we were essentially no different from non-paying tenants (or tenants paying with our data). As time passed, we saw what happened when the landlords started evicting people. Nowadays, even just posting anti-government content can get you kicked out of these centralized platforms in an instant, and you have absolutely no ownership rights over your data or anything else. We've actually been living in a crappy system for a long time, and this system is still going on. From the era I caught onto, ActivityPub w

2026-08-21 原文 →
AI 资讯

VRP Is Ready for External Validation — One Company Can Be the First to Pilot It

VRP Is Ready for External Validation — Who Will Be the First to Pilot It? My name is Vitalijus Riabovas. I am the independent architect and creator of VRP — Veil Routing Protocol . VRP is a continuity-first networking architecture built around a simple principle: A logical session should not have to die simply because the network underneath it changed. Wi-Fi → LTE/5G. IP mutation. NAT / CGNAT churn. Temporary blackout. Path failure. Recovery. Replay attempts. Stale authority. Duplicate execution. For a long time, VRP was primarily architecture, runtime engineering and internal validation. That stage has changed. The public validation boundary exists now. And I am inviting serious engineers and organisations to test it. DON'T TRUST MY CLAIMS. TEST THEM. I am not asking the networking industry to believe a presentation. I built the measurement boundary. The public VRP Validation Kit provides engineers with an environment for evaluating observable behaviour independently. You can: clone the repository; run the Docker scenarios; inspect generated evidence; verify manifests and hashes; attack the evidence; delete events; duplicate events; reorder events; attempt replay; introduce stale conditions; corrupt artifacts; run the verifier; reproduce PASS / REJECT / INCOMPLETE outcomes. If you believe something is wrong, try to produce a reproducible contradiction. Give me: environment → scenario → commands → evidence → result That is useful engineering. WHAT HAS BEEN BUILT? VRP has moved far beyond an architectural diagram. The project now includes multiple engineering layers. Continuity architecture Logical session identity is designed to survive changes in the underlying network path. The architecture is being developed around continuity rather than assuming that transport identity and logical session identity must always be the same thing. Runtime The protected runtime implements the private VRP mechanisms. That implementation is not public . State and transition handling T

2026-08-21 原文 →
AI 资讯

Harper Argues Against the Multi-System Stack and Releases 5.2

The database platform Harper advocates for a single-runtime architecture that keeps application code and data together, with its benchmark against a Vercel-based stack reporting significantly better performance on live, personalized-data workloads. Harper recently released version 5.2, with a new record cache and more throughput per node. By Renato Losio

2026-08-20 原文 →
AI 资讯

Building Distributed Systems in Elixir: Part 6 — Named Processes

In the previous part of this series, we built a tiny supervisor from scratch. When a worker crashed, the supervisor started a replacement. That replacement had a new PID: old worker -> #PID<0.102.0> new worker -> #PID<0.105.0> This reveals an important limitation of sharing PIDs as a public interface. A PID identifies one running incarnation of a process. It is excellent for sending a reply, setting up a monitor, or creating a link. It is not a stable address for a service that may stop and later be replaced. In this part, we'll use named processes to give a worker a discoverable address: :worker We'll build three small examples using: Process . register / 2 Process . whereis / 1 :global . register_name / 2 :global . whereis_name / 1 send / 2 No GenServer . No OTP Registry . The goal is to understand the lookup problem that registries solve before reaching for those abstractions. The PID-Sharing Problem Suppose one process starts a worker and gives its PID to a client: worker = spawn ( fn -> worker_loop () end ) send ( client , { :worker_started , worker }) The client can now send work directly: send ( worker , { :work , self (), "hello" }) This works while that particular worker process is alive. But process IDs are temporary. If the worker exits, the PID is no longer a route to the service: Client Worker holds #PID<0.102.0> #PID<0.102.0> | | | X exits | | send(#PID<0.102.0>, work) |------------------------------> no worker receives it Sending to a dead local PID does not raise an error and does not restart a process. The message is simply not delivered to a living worker. One answer is to tell every client about every new PID after a restart. That spreads lifecycle knowledge throughout the system. Another answer is to make clients depend on a name and resolve that name when sending. Registering a Local Name Our first worker waits for a stop message: defmodule Worker do def start do spawn ( fn -> receive do :stop -> :ok end end ) end end Starting it gives us a PID:

2026-08-19 原文 →
AI 资讯

Presentation: Understanding Progressive Collapse: How To Avoid A Cascading Failure

Sam Newman discusses the concept of progressive collapse in civil engineering and how it applies to distributed systems. Using real-world examples - from the 1968 Ronan Point tower failure to AWS outages - he shares crucial resilience engineering strategies for software leaders. Learn how to strengthen components, isolate failures, and reduce interconnections to prevent catastrophic cascades. By Sam Newman

2026-08-19 原文 →
AI 资讯

My QUIC transport had never once been executed. Here's what happened when I ran it.

I've written before about SMESH, a coordination protocol modelled on mycorrhizal networks — the fungal web that lets trees in a forest warn each other about drought and disease with nothing in charge of the network. Signals diffuse, decay on their own, and get reinforced when independently confirmed. Consensus emerges instead of being orchestrated. That was the idea. This post is about the part where I found out whether it worked. The transport that had never run SMESH has had a QUIC transport in it for a while. Roughly 500 lines: a quinn endpoint that is simultaneously server and client, self-signed certs, length-prefixed bincode frames over unidirectional streams, an accept loop that spawns per-connection and per-stream tasks, connection pooling. Every test passed. The workspace was green. I could point at smesh-runtime/src/transport.rs and say "yes, it does peer-to-peer." Then I grepped for who actually constructed it: $ grep -rn "QuicTransport" --include = '*.rs' . smesh-runtime/src/transport.rs:177:pub struct QuicTransport { smesh-runtime/src/transport.rs:192:impl QuicTransport { smesh-runtime/src/lib.rs:16:pub use transport:: { QuicTransport, ... } ; Its own definition, and a re-export. Nothing else in the workspace had ever instantiated it. No binary opened a socket. SmeshRuntime imported TransportConfig , stored it in a struct field, and never looked at it again. I had a networking layer with tests, docs, and zero executions. Three bugs in the first twenty minutes I wrote an integration test that starts two runtimes, has one dial the other, and asserts a signal crosses. Here is what fell out before it went green. 1. It panicked on the first call. Could not automatically determine the process-level CryptoProvider from Rustls crate features. rustls 0.23 refuses to pick a crypto backend when more than one is compiled in, and quinn pulls in both through its own feature set. Every call to QuicTransport::new would have panicked for anyone, ever. Nobody noticed bec

2026-08-19 原文 →
AI 资讯

Distributed Locking in Practice: Guarantees, Failure Scenarios and Better Alternatives (2/4)

In this article, we'll explore the mechanisms to solve the coordination problem. 8. Introducing Leases To address the problem of permanent ownership, distributed systems typically replace it with temporary ownership. This concept is known as a lease . Instead of granting indefinite control over a resource, the coordination service assigns ownership for a limited period of time. Rather than stating, “You own this resource until you explicitly release it,” the system instead says, “You own this resource for the next 30 seconds.” This changes the interaction model significantly. Acquire Lease | v Execute Work | v Renew Lease | v Continue Processing As long as the application remains healthy, it periodically renews the lease to maintain ownership. If the application crashes or becomes unresponsive, it can no longer renew the lease. Once the lease duration expires, ownership is automatically revoked. At that point, another application becomes eligible to acquire the lease and continue the work. Leases solve a critical problem in distributed systems: they prevent abandoned locks from blocking progress indefinitely . The system can recover automatically without manual intervention. However, while leases improve availability, they also introduce a new class of subtle and more complex problems. Leases Depend on Time To understand the next challenge, assume the lease duration is thirty seconds. Application A successfully acquires the lease. Lease Granted Duration = 30 seconds After twenty seconds, the JVM begins a long Full Garbage Collection cycle. This pause lasts forty seconds, significantly longer than the lease duration. The timeline now becomes problematic. Lease Granted | | Processing | | GC Pause (40 sec) | | Lease Expires While Application A is paused, the lease expires. During this time, another application requests access to the same resource. The coordination service observes that the previous lease has expired and therefore grants ownership to Application B. Appl

2026-08-18 原文 →
AI 资讯

The Outbox Pattern Is Not Enough

The textbook version of the transactional outbox is tight. You save the domain entity and an outbox row in one local transaction. A background scheduler picks up PENDING rows and publishes them to Kafka. You never publish inside the request thread — no dual-write, no atomicity breach. The pattern closes the consistency gap. Then you load-test it. I ran 1,000 authenticated requests through my event-driven platform in 70 seconds. The gateway returned 201 for every one of them. The outbox absorbed every row. The consumer drained everything. By every visible metric the system looked healthy. Underneath that health, I found three production-grade problems the textbook never mentioned. What a correct implementation looks like Before the problems, the shape of the solution. The outbox publisher runs on a @Scheduled virtual-thread worker: @Scheduled ( fixedDelay = 5000 ) @Transactional public void publishPendingEvents () { List < OutboxEvent > batch = outboxRepository . findTop20ByStatusOrderByCreatedAtAsc ( OutboxStatus . PENDING ); for ( OutboxEvent event : batch ) { event . setStatus ( OutboxStatus . PROCESSING ); outboxRepository . save ( event ); try { kafkaTemplate . send ( event . getTopic (), event . getPayload ()). get (); event . setStatus ( OutboxStatus . PUBLISHED ); } catch ( Exception e ) { event . incrementRetryCount (); if ( event . getRetryCount () >= MAX_RETRIES ) { event . setStatus ( OutboxStatus . FAILED ); } else { event . setStatus ( OutboxStatus . PENDING ); } } outboxRepository . save ( event ); } } This is correct. The PROCESSING state prevents another scheduler instance from claiming the same row. The retry cap prevents infinite cycling. The PENDING fallback on transient errors gives the event another chance. The dual-write problem is genuinely closed. Here is what that correctness does not cover. Gap 1: Your throughput ceiling is a config line fixedDelay = 5000 means the scheduler runs every 5 seconds. findTop20 means it picks up 20 rows per cycl

2026-08-18 原文 →
AI 资讯

A Floor Beneath Every Person: Design Choices in the First Social Resource Floor Blueprint

TL;DR — I've been building the Social Resource Floor: an open blueprint for coordinating one person's access to basic survival resources — food, housing, energy, healthcare, and more — across many independent providers, so that reaching those resources is grounded in being human rather than in financial access. The first blueprint version is now complete: language-neutral schemas, prose specifications, a reference implementation, and a first adapter. This post is about the engineering choices behind it, and the reasons for each — how it stays a contract rather than a product, how it keeps personal data out of the coordination layer, why it binds to existing standards instead of inventing new ones, and how I check that the contracts are implementation-independent rather than just claiming they are. The problem the Floor is trying to help with Today, for most people, survival routes through financial access. To reach food, housing, energy, or healthcare you generally need money, and to hold or move money you need banking, employment, or purchasing power. Financial access has become the gate standing in front of the resources a person needs to stay alive. The goal of the Social Resource Floor is narrow and specific: to help make it so that financial status is not the condition that determines whether a person can reach the basic resources required to survive. It does not try to abolish money, banks, or markets — money stays a first-class resource and delivery method. It aims at one thing: a floor beneath which no person should fall, defined locally, reachable regardless of financial circumstances. That's the mission. Everything technical below exists to make that mission buildable by the institutions — governments, municipalities, NGOs, cooperatives, community providers — that would actually run it, without asking any of them to give up their own systems or hand over their data. Where the Floor sits The delivery systems for social protection already exist and are stron

2026-08-14 原文 →
AI 资讯

You know what's worse than not being able to log in?

This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry . You Know What's Worse Than Not Being Able to Log In? Being told everything worked right up until you try to actually use your account. Yes, that was a real bug. And, somehow, I ended up being pulled into another authentication mystery. At this point, I’m starting to think authentication bugs have a personal grudge against me. 😅 In my previous Smash Story , I wrote about a bug where users simply couldn't log in. This time, the problem was sneakier because most of the flow looked completely healthy. The user was approved, the background task ran, the email and SMS arrived, and Cognito had a user. Then the user actually tried to use their account. And everything fell apart. It Started With Two User Pools The authentication setup was fairly large and had evolved over time, so there wasn't one shiny User Pool doing everything. We had an older Cognito User Pool supporting existing authentication flows, including mobile-based signup, while a newer User Pool handled a newer flow where users received an email containing their PIN. Both pools were intentional because they supported different parts of the authentication journey. That wasn't the problem. The interesting part was that the application database had its own representation of a user, while Cognito had another. On top of that, some of the work connecting those two systems happened asynchronously. As long as everyone agreed about who the user was, nobody cared. The moment they disagreed, authentication became very interested. The Tiny Timing Window The problem appeared in the partner and dependant journey. A member could create a partner or dependant during signup or later from the member details area. A relevant non-member user would then approve the account, which scheduled an asynchronous task called SendingEmailsAfterApprovalBot in a TaskList database table. That task ran every 15 minutes, and once it executed, the partner or depend

2026-08-13 原文 →
AI 资讯

Building a Distributed System in Go: Part 1 — In-Process Message Passing & CSP Primitives

Welcome to Part 1 of the Go Distributed Systems Lab series! Over the course of 20 hands-on projects, we are building core distributed systems primitives from the ground up using Go 1.22+ and the standard library ( net , sync , context , log/slog , encoding/binary ). Before jumping into raw socket framing, gossip protocols, or Raft consensus, we need to master the foundational concurrency building blocks inside a single process: Goroutines, Channels, and Communicating Sequential Processes (CSP) . 💡 The Philosophy: Share Memory by Communicating In traditional concurrent programming (like C++ or Java), thread synchronization often relies on shared memory protected by mutexes, lock-free queues, or read-write locks. Go flips this model with a core design principle: "Do not communicate by sharing memory; instead, share memory by communicating." By passing ownership of data structures through Go channels, each pipeline stage operates on isolated memory. This eliminates data races by design without requiring explicit lock management ( sync.Mutex ). 🏗️ Architecture & Component Design In this first module ( 01-message-passing ), we construct a 3-stage data processing pipeline: +------------------+ Job Channel +------------------+ Result Channel +-------------------+ | Producer | -------------------------> | Worker | ------------------------> | Collector | | (Generates Jobs) | (Buffered, cap=10) | (Isolated State) | (Buffered, cap=10) | (Aggregates Data) | +------------------+ +------------------+ +-------------------+ 1. Ingestion Stage (Producer) Generates typed Job values and pushes them into a direction-constrained buffered channel ( chan<- Job ). When generation finishes, it closes the channel to broadcast an end-of-stream signal. 2. Processing Stage (Worker) Consumes from <-chan Job using Go's for job := range in construct. The worker maintains internal execution metrics (e.g., processedCount ) entirely within its local stack scope—no locks required. 3. Collector Stage R

2026-08-13 原文 →