LLD-009 · LOW LEVEL DESIGN
Meeting Room Scheduler
Make interval semantics, atomic conflict checks and local-time recurrence explicit.
Local Java 21 reference verified: 222 checks. Learner drafts are not executed. Author self-review; independent review pending.
Existing introductory lesson · Version 1
Understand the problem
Design room discovery, booking, cancellation, rescheduling and bounded weekly recurrence. Prevent concurrent double booking and preserve the original booking when rescheduling fails. Explain why a weekly local meeting is not always 168 hours apart in UTC.
Before you start
- Half-open intervals
- Ordered maps and predecessor/successor queries
- Atomic check-and-write
- Instant vs local time and timezone rules
Scope and explicit decisions
- One in-memory calendar over configured rooms. No participants, invitations, external calendar synchronization, authorization, persistence, waitlists or series-edit API is implemented.
- Ranges are nonempty half-open [start,end) Instant values. Adjacent bookings are allowed. Need stores positive people count and required equipment; Room capacity/equipment must satisfy it.
- Room availability from find is advisory. book rechecks eligibility and conflicts under the same monitor used by every mutation. No availability token reserves capacity.
- book has no idempotency key. A retry after a successful lost response conflicts instead of returning the original result; clients must reconcile. Reschedule uses an expected version, while cancel of an already cancelled booking is a no-op.
- Reschedule may change room and interval, retaining the original Need and booking ID. The target is validated before removing the source booking. Invalid room/equipment/conflict/version leaves the source unchanged.
- weekly expands 1..52 local weekly starts in a named ZoneId. It rejects local times with zero or multiple valid offsets rather than silently shifting a gap or choosing one side of a fold.
- Each occurrence has a fixed elapsed Duration after its resolved start. This is not a promise that the local end clock will always differ by that duration across a DST transition.
- bookSeries validates all proposed ranges, their internal conflicts and existing room conflicts before publishing any. Expansion and booking are separate: an expanded occurrence is not reserved until the calendar accepts the series.
Worked examples
Input: Room A booked [0,10). Try [10,20) then [9,11).
Outcome: Back-to-back succeeds; overlapping request fails.
Endpoint semantics are part of the domain contract, not a UI formatting detail.
Input: Weekly 10:00 New York beginning March 1, 2026.
Outcome: March 1 and March 8 starts remain 10:00 local but differ by 167 elapsed hours.
Local recurrence and elapsed UTC intervals express different requirements.
Try before revealing
Hint 1
Write one precise overlap predicate and test touching endpoints.
Hint 2
Which earlier or later booking can overlap a candidate in an otherwise disjoint timeline?
Hint 3
Validate every target before releasing an existing reservation.
Requirements and invariants
R1 — Active reservations in one room never overlap. book protects I1.
R2 — Failed rescheduling retains the old booking and its ownership. reschedule protects I2.
R3 — A recurring series either passes every conflict check or creates no bookings. bookSeries protects I1.
R4 — Ambiguous/nonexistent local recurrence starts must be rejected explicitly. weekly protects I3.
I1: Each room timeline contains disjoint active intervals whose map key equals their start Instant.
I2: Every active Booking has one timeline entry; a cancelled record has none, and successful updates increase version.
I3: Each expanded local start resolves to exactly one offset under the chosen zone rules.
Derive the design
A full scan defines correctness; ordered neighbors improve conflict lookup
A simple implementation compares every existing booking with the proposed interval using a.start<b.end && b.start<a.end. It is correct and useful as an independent oracle, but costs O(B) per room. Because accepted room timelines are already disjoint, only the predecessor and successor around the candidate start can overlap. The reference uses a TreeMap and skips the same booking during reschedule.
Alternative: Interval trees support more general overlapping interval collections, but here the no-overlap invariant makes ordered neighbors sufficient. A database implementation needs a transactional exclusion/serialization contract; a UI precheck does not protect against concurrent writers.
Clarify time before choosing an index
Ask about endpoint inclusion, room equipment, recurrence zones and the meaning of duration. An efficient interval tree cannot rescue an ambiguous contract.
Exploit only proven index invariants
Room timelines are disjoint because every mutation preserves admission checks. That is what makes predecessor/successor sufficient. If manual imports can insert overlaps, this proof no longer holds.
Plan atomic updates across source and target
Reschedule validates against a view that ignores its own old interval but still sees every other booking. Remove/insert happens only after all checks pass.
Make recurrence a bounded transaction
Expand candidate instants and validate all of them before committing a series. A failure in the final occurrence should not silently leave the first occurrences booked unless partial success was explicitly chosen.
Responsibilities and ownership
Range
Owns: Validated start/end instants
Methods: overlaps contract
Defines half-open semantics once.
Room / Need
Owns: Immutable capacity/equipment and requested requirements
Methods: Validated records
Eligibility is separate from temporal conflict.
Calendar
Owns: Room timelines, booking identities and atomic mutations
Methods: find, book, bookSeries, reschedule, cancel
One monitor protects validation and index updates.
weekly
Owns: Bounded local-time expansion policy
Methods: weekly(first,zone,duration,count)
No room ownership or persistence; returns immutable candidate intervals.
find is advisory. Every mutation rechecks under the calendar monitor.
- Requested room / time / Need
↓ Expected version for updates
Validate eligibility + neighbors↓ No source removal before validation
Commit timeline and ID record↓ Version identifies accepted state
Return immutable Booking
A series of resolved instants is not itself a reservation until admission succeeds.
- Local start + ZoneId
↓ Reject gaps and folds by policy
Exactly one valid offset?↓ Preserve local start across offset changes
Resolve Instant + elapsed Duration↓ Only then reserve room timelines
Validate whole series
Data model, schema and relationships
Rooms own scheduling resources, bookings own half-open intervals, and an optional recurrence series groups concrete occurrences. A series rule does not replace actual conflict-checked intervals.
Implementation boundary: The local ordered-neighbor scheduler supports bounded atomic weekly series and explicit timezone ambiguity rejection. Durable user/calendar metadata is an extension.
These are proposed logical schemas with field contracts, not executable database migrations. PK identifies a row, FK references an existing parent, UK is a uniqueness rule. A composite PK identifies the combination. Nullable references are marked with ?. A foreign key protects identity; cross-row limits, timing and state transitions also require the transaction rules below.
Connectors name the relationship; multiplicities state parents per child and children per parent. 0..1 means optional, 1 required, 0..* any number. Self-links show two roles of the same entity.
Connectors name the relationship; multiplicities state parents per child and children per parent. 0..1 means optional, 1 required, 0..* any number. Self-links show two roles of the same entity.
Connectors name the relationship; multiplicities state parents per child and children per parent. 0..1 means optional, 1 required, 0..* any number. Self-links show two roles of the same entity.
Entity definitions and field contracts
room
One independently exclusive resource.
| Field | Type / key | Meaning and reference |
|---|---|---|
id | uuid · PK | Room identity |
capacity | integer · VALUE | Participant capacity |
Code mapping: Room identity.
- Capacity positive; features are separate policy inputs.
organizer
Booking owner identity.
| Field | Type / key | Meaning and reference |
|---|---|---|
id | uuid · PK | Organizer identity |
name | text · VALUE | Display name |
Code mapping: External account metadata.
- Authorization is separate from collision detection.
series
Recurrence intent and interpretation.
| Field | Type / key | Meaning and reference |
|---|---|---|
id | uuid · PK | Series identity |
organizer_id | uuid · FK | Owner · organizer.id |
timezone | text · VALUE | IANA zone |
rule | text · VALUE | Bounded weekly recurrence |
Code mapping: Series command/result.
- Reference bounds number of occurrences; reject gap/ambiguous local timestamps.
room_booking
Concrete accepted interval.
| Field | Type / key | Meaning and reference |
|---|---|---|
id | uuid · PK | Booking identity |
room_id | uuid · FK | Exclusive resource · room.id |
organizer_id | uuid · FK | Owner · organizer.id |
series_id | uuid? · FK | Optional recurrence group · series.id |
start_at | timestamp · VALUE | Inclusive start instant |
end_at | timestamp · VALUE | Exclusive end instant |
Code mapping: Booking records and ordered start map.
- start_at<end_at; no overlapping active intervals per room; touching endpoints allowed.
Relationship cardinalities and their reasons
| Parent → child | Multiplicity | FK / why |
|---|---|---|
| organizer → series owns series | 1 parent per child; 0..* children per parent | series.organizer_id → organizer.idOne owner may define many bounded recurrence requests. |
| room → room_booking hosts intervals | 1 parent per child; 0..* children per parent | room_booking.room_id → room.idA room has historical bookings with non-overlap enforcement. |
| organizer → room_booking creates bookings | 1 parent per child; 0..* children per parent | room_booking.organizer_id → organizer.idOwnership is independent of room identity. |
| series → room_booking groups occurrences | 0..1 parent per child; 0..* children per parent | room_booking.series_id → series.idOne-off bookings have no series; a series has concrete accepted occurrences. |
Transaction boundaries
- Book/reschedule: exclude only the booking being moved, validate predecessor/successor overlap and commit removal/insertion together.
- Series: expand bounded instants, check internal/existing conflicts, then commit every occurrence or none.
Indexes and uniqueness
- Ordered index(room_id,start_at) supports neighbor checks for disjoint intervals.
- Use a database range exclusion constraint or room-level lock; a start-time unique index alone does not prevent overlap.
Schema traps and failure cases
- Concurrent different-start inserts can overlap despite unique start timestamps.
- DST interpretation cannot silently shift a requested local time without contract agreement.
Trace the records through one operation
- Book [10:00,11:00); [11:00,12:00) remains legal.
- Reschedule validates target neighbors before removing original interval.
- If occurrence 3 of a weekly series conflicts, none of the new series is committed.
Method contracts
find(Need,Range) → room IDs
Filter current eligibility and conflicts. Results have deterministic room-ID ordering but are advisory snapshots.
book(roomId,Need,Range) → Booking
Revalidate room eligibility and nonoverlap, then create version-zero booking and index entry atomically.
reschedule(id,targetRoom,Range,expectedVersion)
Reject stale/cancelled identity or unavailable target. Compute the replacement before removing the old index; preserve ID/Need and increment version.
cancel(id,expectedVersion)
Active cancellation requires current version; remove its index and retain a terminal historical record. Repeat terminal cancellation returns that record.
weekly(...); bookSeries(roomId,Need,ranges)
Expansion rejects gaps/folds. Series admission sorts <=52 proposed ranges, checks each against existing timeline and adjacent proposed range, then commits all.
What, when, why and how
Each concept is attached to the requirement it protects, its implementation, an alternative and a test.
Half-open time ranges
- what
- Include the start and exclude the end: [start,end).
- when
- One meeting ends exactly when the next begins.
- why
- Endpoint ambiguity can reject valid back-to-back bookings or accept actual overlaps.
- how
- Range validates start<end. overlaps uses strict comparisons in both directions.
Code: Range, overlaps · Invariants: I1
Alternatives, cost and failure example
Alternative: Closed intervals require a different overlap definition and a decision about shared endpoint instants.
Cost: One clear predicate makes tests and indexes simpler; conversion to display time is separate.
Counterexample: Treat [0,10) and [10,20) as overlapping using <= comparisons.
Test: Accept adjacent intervals and reject [9,11) against either neighbor.
Ordered-neighbor conflict detection
- what
- Use the established nonoverlap invariant to inspect only nearby reservations.
- when
- A room has many bookings and admission is frequent.
- why
- A full scan repeats unnecessary comparisons, while checking only the next start misses a containing predecessor.
- how
- Find floorEntry(start) and ceilingEntry(start); during reschedule skip the original booking in both directions. Test either retained neighbor with overlaps.
Code: conflict, reschedule · Invariants: I1
Alternatives, cost and failure example
Alternative: A list scan is simpler for tiny calendars and remains the test oracle. An interval tree is warranted only for richer overlap queries.
Cost: O(log B) neighbor lookup per room; series sorts O(S log S) and checks S neighbor pairs under the lock.
Counterexample: New [5,6) lies inside old [0,10); checking only a later start misses it.
Test: Compare indexed admission with a seeded independent full-scan model across varied intervals.
Validate target before releasing source
- what
- Reschedule is one atomic transfer of ownership.
- when
- A room/interval change can fail or race another booking.
- why
- Deleting the old booking first loses a valid reservation when the target is unavailable.
- how
- Validate expectedVersion, eligibility and target conflict with the current record ignored. Only then replace old/new timeline entries and booking snapshot under one monitor.
Code: reschedule, bookSeries · Invariants: I2
Alternatives, cost and failure example
Alternative: A durable database can use one transaction, but separate API calls to cancel then create do not provide the same guarantee.
Cost: A single calendar lock is easy to reason about but limits parallel room throughput. Per-room locks need ordering for cross-room changes.
Counterexample: Cancel source, then discover target lacks a screen or overlaps another event.
Test: Assert failed target validation leaves the original interval; race two same-room bookings and get one winner.
Recurrence as local rule plus explicit zone policy
- what
- Resolve weekly local starts in a named timezone, then derive instants.
- when
- Meetings span clock-offset changes.
- why
- Adding 168 hours in UTC can shift the local clock time, and default normalization can silently move nonexistent times.
- how
- Use LocalDateTime.plusWeeks, inspect getValidOffsets and require exactly one. Derive end by fixed elapsed Duration. Keep expansion bounded and separate from room admission.
Code: weekly · Invariants: I3
Alternatives, cost and failure example
Alternative: A product may choose earlier/later offset for folds or skip gap occurrences, but that must be visible policy and independently tested.
Cost: Zone-rule versions can change; persisting only future instants loses recurrence intent, while persisting only rules can change expansion after updates.
Counterexample: March 1 to March 8 at 10:00 New York spans 167 UTC hours in the tested zone rules.
Test: Reject March 8 02:30 gap and November 1 01:30 fold; preserve 10:00 across spring transition.
Read and run the Java reference
Reveal complete Solution.java
Java 21. Save as Solution.java; compile with javac --release 21. The repository also contains Test.java and the lld:java verification command. This reference implements the local contract above.
conflict checks both temporal neighbors
An earlier containing booking and a later-starting overlap are different cases. Skipping the original once per direction supports self-overlapping reschedules without losing the adjacent real conflict.
reschedule builds before mutating
The new Booking and incremented version are ready before removing the original timeline entry. Routine validation failures leave the old booking usable.
weekly rejects automatic clock normalization
getValidOffsets exposes gaps/folds instead of relying on convenience methods that choose or shift a local time implicitly.
bookSeries validates its own ranges too
Existing calendars can be conflict-free while the requested series overlaps itself. Sort and compare neighboring proposed ranges before any insertion.
Complexity and limits
- find checks R rooms with O(log B) temporal lookup each, plus equipment subset checks; results are in configured sorted-ID order.
- book/cancel/reschedule update ordered maps in O(log B), with expected O(1) booking-ID lookup. All run under one monitor.
- bookSeries sorts S<=52 ranges and validates O(S log B+S log S) before committing O(S log B). Expansion costs O(S) zone resolutions for the bounded rule.
- Space grows O(B+history+R); cancelled records remain for identity/reconciliation. This reference has no retention cleanup or persistence.
Walk through the actual reference
These walkthrough fixtures are emitted only after the Java harness verifies their operations and expected state. Concurrency tests additionally exercise competing callers.
Half-open intervals and safe reschedule
Step 1 of 2: Book [0,10), then [10,20)
Both accepted in room A
Invariant: Touching endpoints do not overlap
Read the complete trace table
| Action | State after action | Invariant |
|---|---|---|
| Book [0,10), then [10,20) | Both accepted in room A | Touching endpoints do not overlap |
| Reschedule first to [15,25) | Rejected; original interval retained | Validation precedes namespace mutation |
Weekly local time crosses DST
Step 1 of 2: Expand March 1 and March 8 at 10:00 New York
Starts stay 10:00 local, UTC gap 167 hours
Invariant: Weekly local time is not a fixed 168-hour UTC interval
Read the complete trace table
| Action | State after action | Invariant |
|---|---|---|
| Expand March 1 and March 8 at 10:00 New York | Starts stay 10:00 local, UTC gap 167 hours | Weekly local time is not a fixed 168-hour UTC interval |
| Attempt March 8 at 02:30 | Rejected: nonexistent local time | No silent timezone normalization |
Mistakes and counterexamples
Use closed endpoints accidentally
Failing case: A meeting at 10 starts exactly when the previous ends.
Repair: Choose and test half-open intervals explicitly.
Only perform an availability read
Failing case: Two callers see a free room and then both write.
Repair: Put final conflict check and insertion under one authoritative atomic boundary.
Cancel then create to reschedule
Failing case: The target conflicts after source deletion.
Repair: Validate target first and transfer ownership atomically.
Repeat by adding fixed UTC weeks
Failing case: The local start time changes after DST.
Repair: Store local rule/zone and resolve each occurrence with declared gap/fold handling.
Engineer → Senior → Staff
Implement precise booking semantics.
Executed: eligibility, half-open overlap, discovery, booking and cancellation.
Change exercise
Add containment, adjacency and invalid-empty-range tests.
Worked answer guidance
Use strict overlap comparisons. A containing predecessor must be checked even if no future booking starts inside the proposed interval.
Interview follow-ups
Why store Instants in the timeline?
They identify actual moments for conflict comparison. User-local formatting and recurrence rules are resolved at a separate boundary.
Does find reserve a room?
No. It is advisory; final admission must recheck under the authoritative owner.
Your design attempt
Each language has a separate draft across all three levels. Saving is device-only and does not sync to your account. Anyone using this browser profile can load it. Loading is always explicit.
Java 21 reference code is provided for local study. Use Run program in the editor to execute your code and test driver. Program runs show output; they do not grade your design.
Run program · Java 21
No unsaved changes · 208/20000
Self-review
Checks apply to this visit. Self-review is not a passed submission or verified mastery.
Provenance and scope of evidence
Original teaching material. The local contract is implemented by Solution.java and tested by Test.java on Java 21. Distributed persistence, real provider/device adapters and deployment are not claimed.
Reference verification is not independent editorial review, production deployment verification or a claim that distributed adapters are implemented.