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.

AreaRole
IdentityStores installation-level signals
SubscriptionTracks APNs device token and subscription state
PreferenceUser intent by park, parking lot, and notification type
Notification factNormalized candidate facts from source data
Fact claimPrevents the same fact from being processed twice
Delivery decisionRecords why something was sent or blocked
OutboxDurable queue waiting for actual push delivery
Delivery attemptRecords delivery responses and failures
Redis indexSupporting 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

Comments

    Image preview