← Back
Home › Systems › Productivity and collaboration

calendar

Stores events that happen at set times, repeat, and involve several people and rooms, and keeps every participant's view consistent.

Google Calendar

Open the calendar pagecalendar Hdr histogramdata-structures: Log-linear bucketed histograms with bounded relative error for latency percentilesInterval treedata-structures: Index of ranges that answers "which intervals overlap this point or range?" in O(log n + k); static sorted-array form is the compact, cache-friendly choice.›Deterministic simulationtestingdistributed: Run the whole system in one thread under a seeded scheduler with simulated network, disk and clock, so every failure replays from its seed.›Incremental sync cursorsdistributed: The client keeps one opaque token; each pull returns only the changes after it plus the next token, so sync cost scales with what changed, not with the dataset.›Primary-backup replicationdistributed: One node orders all writes and ships them to standbys; the acknowledgement rule (none, one, all) sets what a crash loses, and an external arbiter decides who takes over.›Process supervisionhardware-os: A parent process starts services, reaps their exits and restarts them under a delay and burst-limit policy, then stops them with SIGTERM before SIGKILL.›Email delivery smtpnetworking: Outbound SMTP: queues, MX lookup, retries, bounces, feedback loops and IP warm-upMobile push deliverynetworking: Sending notifications through APNs and FCM: HTTP/2 requests, token hygiene, collapse keys, TTL and priority, and the retry rules that keep you un-throttled.›Webhook deliverynetworking: Reliable server-to-server HTTP callbacks: signed POSTs, 2xx-means-done, retries over days with jitter, endpoint disabling, SSRF guards and idempotent receivers.›Constraint propagation(constraint programming)optimization: Variables with finite domains, propagators that prune impossible values to a fixpoint, and backtracking search with trail undo between decisions.›Authorization models (RBAC,ABAC, ReBAC)security: How to decide who may do what to which object; roles for coarse rules, relationships for sharing, and checks on every object.›Coverage-guided fuzzingsecurity: Mutate inputs, run an instrumented target, and keep any input that reaches a new code edge; feedback turns blind guessing into incremental search.›Mutual TLSsecurity: TLS where the client also proves a certificate key, so every connection carries a verified, rotatable machine identity.›Icalendar formatstorage: ICalendar (RFC 5545) objects, VTIMEZONE and iTIP scheduling messagesOptimistic concurrency controlstorage: Run transactions without locks, record what they read, and validate at commit that nothing changed; fastest when conflicts are rare.›Point-in-time recoverystorage: Periodic base backups plus a continuously archived change log let you rebuild the state as of any instant, then branch from there.›Calendar date arithmeticsystems: Civil dates, time zones (tzdb), DST gaps, month-end anchoring and duration mathGraceful drainingsystems: On a termination signal, stop taking new work, finish in-flight work until a deadline, tell clients to reconnect elsewhere, then exit; clients resume from a token.›Hierarchical timing wheelsystems: Timers bucketed by expiry into wheels of slots, coarser wheels for far deadlines: O(1) insert and cancel for millions of timeouts.›Recurrence rule expansionsystems: Expand an RFC 5545 RRULE lazily, in local wall-clock time, into concrete instants; store the rule, materialise only a bounded horizon.›Cooperative schedulingconcurrency: Green threads/coroutines yielding at await points on a user-space schedulerFault injectiondistributed: Inject latency, errors, kills and partitions via proxies, netem or hooksRecord replaydistributed: Record every nondeterministic input (time, randomness, I/O, model and tool outputs, per-tick player input) and re-execute deterministically for debugging, replays and state rebuildMessage sequence numbersdistributed: A per-stream counter on every message lets the receiver detect loss, duplicates and reordering, ack cheaply and resume after reconnect with "send from N".›Cursor paginationnetworking: Keyset/opaque-cursor pagination for stable incremental reads from APIs and tablesLeasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›Health checkingsystems: Decide per backend whether to route to it using probes and real-traffic errors, with consecutive-count hysteresis, capped ejection and a panic fallback.›Bounce processingnetworking: Parse DSNs and feedback loops, classify hard/soft bounces, maintain suppression listsSPF, DKIM and DMARC senderauthenticationnetworking: SPF authorizes sending IPs, DKIM signs the message, DMARC requires either to match the visible From domain and says what to do on failure.›DNS message format and clientlookupsnetworking: Binary query/response format of DNS (RFC 1035) and how a stub client uses it: record types, name compression, UDP/TCP/EDNS sizes, TTL caching.›Smtp protocolnetworking: SMTP/ESMTP sessions, pipelining, STARTTLS and reply codesTLS 1.3security: The standard encrypted, authenticated transport; one round trip, only forward-secret AEAD suites, and little left to misconfigure.›+ 22 moreTransparent interceptionnetworking: Redirect traffic to a local proxy (iptables TPROXY/REDIRECT, eBPF) without app changesSyscall interceptionsecurity: Intercept syscalls via ptrace, seccomp user notify or a user-space kernelFencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›Linear-time regular expressionmatchingsearch-sort: Match a regular expression in O(pattern x text) worst case by tracking all live parse positions at once, then cache the state sets as a lazily built DFA for speed.›Mime parsingstorage: RFC 5322/2045 headers, multipart bodies and transfer encodingsSliding-window counterssystems: Count events in the last W seconds with a ring of sub-bucket counters; the two-bucket weighted estimate gives rate limits in 16 bytes per key.›AEAD (AES-GCM,ChaCha20-Poly1305)security: Authenticated encryption that encrypts and detects tampering in one call; the only kind of symmetric encryption you should use.›Cryptographic hashes (SHA-2,BLAKE3)security: Collision-resistant fingerprints of data; SHA-256 for interoperability, BLAKE3 for speed, HMAC/HKDF for keyed uses.›ECDH key exchange (X25519,P-256)security: Each side multiplies the other's public curve point by its own scalar and both get the same point; always validate the peer key and run the result through a KDF.›HKDF key derivation(extract-and-expand)security: HMAC-based two-step key derivation: extract a uniform pseudorandom key from any secret, then expand it into as many context-bound keys as you need.›X509 certificatessecurity: Certificate chains, path validation, SANs, revocation and short-lived issuance+ 6 moreEbpfhardware-os: Verified in-kernel programs on hooks (kprobes, XDP, tc) with mapsPtrace process controlhardware-os: Ptrace: attach, read and write registers and memory, single-step, intercept signalsSeccomp bpfsecurity: Syscall filtering with seccomp BPF programsRecursive-descent parsingcompilers: Hand-written top-down parser with one function per grammar rule; simple, fast, with full control over error messages and recovery.›Aho-Corasick multi-patternmatchingsearch-sort: Find all occurrences of many fixed patterns in one pass over the text, in time independent of the pattern count, using a trie with failure links.›SIMD substring searchsearch-sort: Find bytes or substrings by comparing 16-64 haystack positions per instruction against the needle's first and last byte, then verifying the rare candidates.›LRU and CLOCKsystems: LRU evicts the least recently used entry via hash map plus linked list; CLOCK approximates it with a reference bit, so hits take no lock.›Constant time codesecurity: Writing secret-dependent code without branches or memory lookups on secrets; constant-time compare and selectCsprngsecurity: OS-backed cryptographically secure random numbers (getrandom), and when a fast PRNG is not enough+ 5 more+ 1 more

How it is built

Architecture overview

A calendar system stores events that happen at a time, may repeat forever, involve several people and rooms, and must show up identically on a phone, a laptop and a third-party app. Its core is a small event store plus three pieces of time logic that every user-visible feature depends on: recurrence expansion (turning "every second Tuesday" into concrete instants), time-zone arithmetic (wall-clock times that move relative to UTC across daylight saving), and interval queries (what overlaps this week, who is free). Around that core sit a scheduling service that proposes meeting times, an invitation channel that talks to other calendars by email and CalDAV, a sync API that ships only changes to clients, and a reminder service that fires notifications on time. The idea that makes it work is to store the rule and the local time, expand lazily inside the window being looked at, and never write a million future instances.

Parts:

 phone / web / CalDAV client ──HTTPS──► API (authz, sync tokens) ──► event store (SQL, versioned rows)
          ▲  push (APNs/FCM)                 │                              │ change log
          │                                  │ window query                 ├──► free/busy index (intervals per calendar)
   reminder service ◄── timing wheel ◄───────┤ expand RRULE in window       ├──► webhooks / integrations (retry, DLQ)
          ▲                                  ▼                              └──► invitation mailer ──SMTP──► other calendars
          └────────────── scheduler (find a time, book a room) ◄── availability index          ▲ replies (iMIP/iTIP)

The write side (create, edit, respond) is small and transactional; the read side (month views, free/busy) is windowed, cacheable and tolerant of a few seconds of delay. Both query by time window, never by "all events".

How the parts fit together

Event store

An event row holds the master: start (wall-clock time plus a zone name, or a date for all-day events), duration, title, location, the recurrence rule if any, a list of excluded dates, attendees with their reply status, and a version. A one-off exception ("this Tuesday moved to Wednesday") is a second row that points at the master and the instance it replaces. This is the shape of the iCalendar format (no page yet): RFC 5545 defines the event component (VEVENT) with RRULE, EXDATE, RDATE and RECURRENCE-ID, and every other calendar speaks it, so the store keeps the iCalendar fields rather than inventing its own.

Two people editing the same event is the main concurrency case, and it is rare enough for Optimistic concurrency control: each update names the version it saw (CalDAV exposes this as the ETag with If-Match), and a mismatch returns the current copy to merge. Whole-series edits ("change the time of all future instances") split the master in two at the chosen instance instead of rewriting history. The database's Write-ahead log gives crash safety and also supplies the ordered change feed that every downstream piece reads.

Time engine

Recurrence rule expansion turns a rule into instants lazily: given a window, it walks the rule forward from the start in local wall-clock time and yields only instances inside the window. Storing the rule and not the instances keeps "every day, forever" at one row, and makes "change all future events" an edit of one field. The hard part is not the iteration but the calendar arithmetic underneath it (Calendar date arithmetic (no page yet)):

  • Local time first. "Every Monday 09:00 in Berlin" is expanded as local times and only then converted to UTC, so the meeting stays at 09:00 across the DST change while its UTC time moves by an hour.
  • Gaps and overlaps. A local time that does not exist (clocks jump forward) or occurs twice (clocks go back) needs a defined rule; RFC 5545 specifies one for each, and the library must follow it.
  • Month-end anchoring. "Monthly on the 31st" skips short months in RFC 5545; "last day of the month" is a different rule (BYMONTHDAY=-1). Products often pick the second meaning silently, which breaks interoperability.
  • Time-zone data. Zone rules come from the IANA tz database and change by government decree; the server updates tzdata independently of the application and stores a zone name, never a fixed offset, for future events.

Availability index

Month views and free/busy both ask "which intervals overlap this window". Scanning every event of every calendar is linear in history. An Interval tree answers an overlap query in O(log n + k) for k results; for a mostly-static index a sorted array of start times with a running maximum of end times is smaller and faster. The index is built from expanded instances for a rolling horizon (order of the next one to two years) and from the rule for anything beyond it. Free/busy answers expose only start and end plus a busy flag, not titles, which is how a colleague can see you are busy without seeing why. Intervals are half-open, so an event ending at 10:00 and one starting at 10:00 do not conflict; where fractional or floating times appear, Interval arithmetic (no page yet) rounds outward to stay conservative.

Scheduler

"Find 45 minutes this week for these six people and one room, avoiding lunch, preferring mornings" is a constraint problem, solved by Constraint propagation: each attendee's free slots are a domain, working hours and room capacity prune it, and a search picks the best surviving slot. Propagation first discards impossible slots cheaply; only the remaining search needs backtracking. For booking many meetings at once, the solver can reduce to SAT, where Two watched literals keeps unit propagation cheap. A tentative plan must be undoable when one placement fails, which a Journaled state revert (no page yet) provides without copying the whole schedule. Nothing is written until the user confirms; the booking is a version-checked write, so two people booking one room race on the room's row and exactly one wins.

Invitations and external calendars

When an organizer adds an attendee, the system must tell that attendee's calendar, which may be a different product. RFC 5546 (iTIP) defines the message types (request, reply, cancel) and RFC 6638 defines how CalDAV servers exchange them internally; between organizations the usual transport is an email with an iCalendar part (iMIP). Delivery therefore inherits everything about email: Email delivery over SMTP (no page yet) with queues and retries over the SMTP protocol (no page yet), recipient domains found via the DNS protocol, links protected by TLS 1.3, and sender reputation held up by DKIM, SPF and DMARC. Invitations that bounce are handled by Bounce processing (no page yet): a hard bounce marks the attendee as unreachable instead of retrying forever. Replies coming back (accept, decline) are matched to the event by its UID and applied as an attendee status change, not as a new event. Only accept a reply whose sender matches the attendee on the event, or anyone can mark anybody as accepted.

Sync API

Clients keep a local copy of the calendars they show. Instead of re-reading everything, each calendar has a sync token; the client sends the last token and receives the changed and deleted event ids plus a new token (Incremental sync cursors). The same idea appears in CalDAV (RFC 6578 defines collection synchronization for WebDAV) and in Google Calendar's sync-token API, which tells a client with an expired token to discard its copy and do a full sync (HTTP 410 Gone; Google's sync guide). Large first syncs page with Cursor pagination (no page yet) so a concurrent edit cannot shift the page boundary, and the token is derived from the change log's position, which is monotonic. Push notifications carry only "something changed in calendar X"; the client then pulls. Per-device streams use Message sequence numbers to detect a missed notification.

Reminders and notifications

A reminder is "alert 10 minutes before an instance". The service keeps the next due alert per event, not all future alerts, and stores the due time in a Hierarchical timing wheel: O(1) insert and cancel for millions of timers, with timer nodes carved from a Slab allocator. When a timer fires, the notification goes to Mobile push delivery (APNs and FCM over HTTP/2, the provider authenticating with a JWT), with a short TTL, because a reminder that arrives after the meeting is noise. A Token bucket per user caps floods (an import of 10,000 events must not send 10,000 pushes). After each firing the engine expands the rule to find the next occurrence and re-arms. Timer state is rebuilt from the store on restart, so the wheel is a cache, not a database. Lateness (the gap between a reminder's due time and the moment it is handed to APNs or FCM) is recorded in an hdr-histogram (no page yet), so the tail percentiles of that gap are visible and a slow wheel or a backed-up push worker shows up before users notice late alerts.

Integrations

Third-party apps want to know when events change (video-call links added to meetings, CRM updates). The system sends them through Webhook delivery: payloads signed with HMAC request signing (no page yet), failures retried on a Durable job queue with Exponential backoff with jitter, and messages that never succeed moved to a Dead-letter queue. Idempotency keys let a caller retry "create event" without creating two, and a Circuit breaker stops hammering an endpoint that is down so one slow customer cannot fill the queue.

Access and operations

Calendars are shared in layers: free/busy only, read details, edit, manage sharing, plus delegation ("my assistant accepts invites for me") and resource calendars. Authorization models fits: roles per calendar, relationships for individual shares, and a rule that the least-permissive answer wins when a user is in conflicting groups. Service-to-service calls (API, scheduler, mailer, push) use Mutual TLS. The store uses Primary-backup replication and Point-in-time recovery, the answer to "I deleted my whole calendar yesterday". Processes run under Process supervision, and a deploy drains the push workers with Graceful draining so no reminder is half-sent. The recurrence engine and the iCalendar parser are tested with Deterministic simulation testing (a simulated clock jumping across DST) and Coverage-guided fuzzing, because parsers read bytes from strangers' invitations.

Data and control flow

Creating a weekly meeting and inviting five people:

  1. The client sends title, start in local time with zone, FREQ=WEEKLY, attendees and an idempotency key.
  2. The API checks the caller may write to this calendar, stores the master row (version 1) in one transaction and appends to the change log. Order of a few milliseconds; the dominant cost is the commit sync to disk.
  3. The log feeds three consumers: the free/busy index (expands instances for the horizon and inserts intervals), the invitation mailer (one iTIP request per external attendee, internal attendees get the event directly) and the reminder service (arms the first alert).
  4. Each attendee's reply is a status change on the attendee list; the organizer's client receives it via its sync token.

Opening the week view on a phone:

  1. The phone has a local copy and a sync token. It sends the token; the server returns events changed since then.
  2. The phone expands recurring masters for the visible week locally (the same rule library, so results match) and draws the grid. Time goes to the one network round trip, not to computation.
  3. If the token has expired the server answers "gone" and the phone refetches the calendar in pages.

Finding a time for a team:

  1. The scheduler asks the availability index for each attendee's busy intervals in the candidate range.
  2. It builds slot domains from working hours minus busy time, intersects them, prunes with room and preference constraints, and ranks the survivors.
  3. The user picks one; the booking is an ordinary version-checked write, so a slot taken in the meantime fails cleanly and the scheduler is re-run.

Scaling

  1. Free/busy fan-out. Looking up thirty people multiplies reads. Keep busy intervals in a compact per-calendar index, cache them with short expiry, batch lookups into one request.
  2. Recurrence cost. Expansion is cheap per window but unbounded rules, many exceptions and large windows add up. Cap the horizon, cache expanded windows, and reject malformed rules (COUNT of millions) at write time.
  3. Reminder volume. Alerts cluster at the top and half of each hour. Spread work across wheel shards and keep push connections open (HTTP/2 multiplexing); a thundering herd at :00 is the peak that sizes the push tier.
  4. Sync traffic. Mobile clients poll; replace polling with push-then-pull, and make "nothing changed" a cheap answer (compare the token to the log head).
  5. Storage. Event rows are small (order of a kilobyte). Shard by calendar owner or workspace; only free/busy crosses calendars.

Failure modes

FailureSymptomCauseFix
Meeting shifts by an hour after DSTrecurring event appears at 08:00 or 10:00instances stored in UTC, or offsets stored instead of zone namesstore local time plus zone; expand in local time
Missing instancea monthly event skips a month"31st" rule on a 30-day month, or a gap-time instance droppedfollow RFC 5545 semantics, test month ends and DST gaps
Duplicate event on clientthe same meeting twice after syncserver UID not preserved on import, retry without idempotency keystable UID, idempotency keys, upsert by UID
Forged acceptan attendee shows "accepted" they never sentiTIP reply trusted without checking sendermatch reply sender to attendee; require DKIM/SPF alignment
Lost updateone person's edit overwrittenwrites without version checkoptimistic concurrency with ETag / If-Match
Reminder stormmany alerts at once, or alerts for deleted eventsno rate limit, timers not cancelled on deletetoken bucket, cancel on change, rebuild wheel from store
Tzdata driftevent times wrong after a government changes DSTold tzdata on some serversship tzdata updates independently, version it

Real implementations

namewhat it isrepolicencenotes
Radicalesmall CalDAV/CardDAV server, file storageKozea/RadicaleGPL-3.0simplest complete example of the server side
sabre/davPHP WebDAV, CalDAV, CardDAV frameworksabre-io/davBSD-3-Clausebase of Baïkal and used by Nextcloud
Baïkallightweight CalDAV/CardDAV server on sabre/davsabre-io/BaikalGPL-3.0self-hosted calendar
Nextcloud Calendarweb calendar appnextcloud/calendarAGPL-3.0client of the Nextcloud CalDAV server
ical4jJava iCalendar parser, builder and recurrence libraryical4j/ical4jBSD-3-Clausethe common JVM recurrence engine
python-caldavPython CalDAV client librarypython-caldav/caldavApache-2.0shows the client side of RFC 4791
rrule (JS)recurrence rules in JavaScriptjkbrzt/rrulesee repoRFC 5545 rules; last push 2024, treat as stable rather than active
Cal.comscheduling and booking pagescalcom/cal.comMITavailability and booking on top of external calendars
Stalwartmail server that also speaks CalDAV and JMAPstalwartlabs/stalwartAGPL-3.0 (see repo)integrates invitations and calendar in one server
Apple Calendar and Contacts Serverhistoric reference CalDAV serverapple/ccs-calendarserverApache-2.0archived; the reference for RFC 6638 behaviour

The alternative to CalDAV is a proprietary JSON API with sync tokens (Google Calendar) or the JSON data model JSCalendar (RFC 8984) for new designs. A closed API is easier to evolve and faster on mobile; CalDAV buys interoperability with every desktop and phone calendar. Most real systems expose both.

Sources

  • RFC 5545, Internet Calendaring and Scheduling Core Object Specification (iCalendar). rfc
  • RFC 5546, iCalendar Transport-Independent Interoperability Protocol (iTIP). rfc
  • RFC 4791, Calendaring Extensions to WebDAV (CalDAV). rfc
  • RFC 6638, Scheduling Extensions to CalDAV. rfc
  • RFC 6578, Collection Synchronization for Web Distributed Authoring and Versioning (WebDAV). rfc
  • RFC 8984, JSCalendar: A JSON Representation of Calendar Data. rfc
  • Google, Synchronize resources efficiently (Calendar API sync tokens). guide

Similar systems

Share the most building blocks with this one.