Filtering Notification Candidates Before APNs Delivery
A push pipeline that records candidates, suppression rules, outbox work, and APNs results separately
Sending a push requires an APNs token together with user intent, places of interest, notification type, quiet hours, source-data freshness, duplicate prevention, and delivery audit records.
Hangangjari notifications select information that can reduce failure during a Hangang outing. Each candidate is evaluated against both delivery and suppression conditions.
A push can reach users before they open the app. A source-data change becomes an unnecessary interruption when it does not match their places of interest and notification settings, so delivery policy runs before APNs delivery.
Records Before Push Delivery
On the server, records before and after delivery are separated like this.
| Area | Role |
|---|---|
| Identity | Stores installation-level signals |
| Subscription | Tracks APNs device token and subscription state |
| Preference | User intent by park, parking lot, and notification type |
| Notification fact | Normalized candidate facts from source data |
| Fact claim | Prevents the same fact from being processed twice |
| Delivery decision | Records why something was sent or blocked |
| Outbox | Durable queue waiting for actual push delivery |
| Delivery attempt | Records delivery responses and failures |
| Redis index | Supporting index for fast candidate lookup |
Postgres keeps reference and audit records. Redis helps candidate lookup and stream speed.
From Candidate to Delivery
sequenceDiagram autonumber participant iOS as iOS app participant API as Push API participant PG as Postgres participant Redis as Preference index participant Facts as Notification facts participant Worker as Push worker participant APNs as APNs iOS->>API: Register subscription and notification settings API->>PG: Store installation / subscription / preferences API->>Redis: Refresh fast candidate lookup index Facts->>PG: Store normalized notification candidates Worker->>PG: Claim candidates to process Worker->>Redis: Find target subscriptions quickly Worker->>PG: Evaluate delivery policy and record decision Worker->>PG: Add sendable items to outbox Worker->>APNs: Send to APNs APNs-->>Worker: Return delivery result Worker->>PG: Record attempt result and follow-up action
A delivery decision evaluates user settings and candidate freshness, then records whether the candidate was sent or suppressed. Candidates that pass enter the outbox and APNs delivery stage; the rest are stored with suppression reasons.
flowchart LR
Fact["Notification fact"] --> Claim["Fact claim"]
Claim --> Candidates["Candidate lookup<br/>Redis first<br/>Postgres fallback"]
Candidates --> Policy["Delivery policy<br/>preferences<br/>quiet hours<br/>cooldown<br/>freshness"]
Policy --> Decision{"Delivery decision"}
Decision -->|push_now| Outbox["Push outbox"]
Decision -->|suppressed| Suppressed["Suppressed decision"]
Outbox --> APNs["APNs"]
APNs --> Attempts["Delivery attempts"]
Suppression Rules Applied Before Delivery
Sending every change would deliver signals a user did not request. Congestion changes, a sharp drop in a favorite parking lot, and control notices for a watched park follow different delivery rules based on user preferences.
Internal fact types are grouped into notification units selected by the user.
- Important changes.
- Parking failure prevention.
- Events and notices for watched parks.
- Quiet hours.
- Repeated-notification cooldown.
- Delivery hold based on source data freshness.
Candidates derived from stale source data are not sent immediately. Notifications that can change a user’s travel decision use stricter freshness thresholds.
APNs as the Final Step
After APNs device-token registration, the server has to keep handling changes to token and subscription state.
Production operation must handle these states:
- Device tokens can refresh or become invalid.
- The same installation can move through several subscription states.
- Users can turn permission off and on.
- Delivery failure can be temporary or permanent.
- The same notification candidate can be sent twice after a worker restart.
The outbox and delivery attempts are separate records. The outbox is “work to send.” An attempt is “a record that we tried to send.” APNs responses update both audit records and device-token state.
An invalid device token, quiet-hours suppression, stale source data, and a temporary APNs failure require different follow-up actions. Delivery decisions and attempts retain those reasons.
Permission After the Reason Is Clear
Hangangjari asks for notification permission after the user selects a park or parking lot and a notification type. Before the system prompt, the app explains the notification’s purpose and timing.
The notification settings UI shows parks and notification types rather than notification_fact_type values.
Operating Records for Delivery and Suppression
Postgres retains user intent, delivery decisions, outbox work, and delivery attempts, while Redis indexes support candidate lookup. Operators review delivery results and suppression reasons together to choose token cleanup, retry, or policy changes.
Comments
No comments yet. Be the first to leave one.
Pending review