Skip to content

Rebase fork onto upstream rpush v9.2.0 - #11

Closed
rstojano wants to merge 33 commits into
masterfrom
rstojano/rpush-fork-diff
Closed

Rebase fork onto upstream rpush v9.2.0#11
rstojano wants to merge 33 commits into
masterfrom
rstojano/rpush-fork-diff

Conversation

@rstojano

Copy link
Copy Markdown

Summary

Rebases the Thanx fork onto upstream rpush v9.2.0. The fork previously sat on a pre-8.0.0 master snapshot (version 7.1.0, fork point 2024-09-02). This replays our 7 custom commits on top of the latest stable upstream release.

Upstream changes pulled in

  • v8.0.0: FCMv1 support. Stops silencing content-available APNs notifications. Drops Ruby 2.4–2.6 and Rails 5.2.
  • v9.0.0 (breaking): Removes legacy APNSv1 binary protocol. Removes GCM entirely. Drops all Ruby 2.x.
  • v9.1.0: Rails 7.1 support. FCM iOS badge support. sqlite3 test support.
  • v9.2.0: Rails 7.2 support.

Rebase details

  • 7 custom commits replayed cleanly (APNs2/APNsp8 connection-reset retries, structured push logging, ActiveRecord app fetch, ECONNRESET retry).
  • 5 duplicated commits auto-dropped (patches already upstream).
  • Skipped dc5137f "Allow Rails 5.1" — obsolete, contradicts v9.2.0's activesupport >= 6.0.

Testing

  • Changed-file unit specs: 83 examples, 0 failures.
  • Full unit suite: 671 examples, 1 failure — configuration.rb Modis redis delegation (untouched file, environment issue).
  • Functional suite failures trace to a DatabaseCleaner vs Rails 7.2 incompatibility in the local dev lock, not to code changes.

Action items before merge

  • Requires Ruby 3.x.
  • Remove all GCM references (apps and notifications) — GCM is gone in v9.
  • Regenerate Gemfile.lock for the target Ruby/Rails; upstream's committed lock pins Rails 7.0.2.2.

benlangfeld and others added 30 commits September 4, 2024 17:48
I sort of just guessed at this method of getting more advanced data into
the APNS body and it worked, figured it'd be better if it was explicit.

Co-authored-by: Ben Langfeld <blangfeld@powerhrg.com>
Fixes rpush#588
Fixes rpush#598
Fixes rpush#683

Major release because of reduced compatibility with Ruby/Rails versions.
Still supporting some upstream unsupported versions for now to make it
easier for users of rpush to upgrade.

https://endoflife.date/rails
https://endoflife.date/ruby
The binary interface was shut down by Apple. Fixes
rpush#568.

Also makes the test suite more reliable and faster.

Re-run of rpush#614.
This was shut down by Google in August 2024 and replaced by FCM
(supported in RPush 8.0.0).
Closes rpush#703 

### Background on the change
FSM messages should be consistent with:
https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Message

These messages support a "apns" key:
https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#ApnsConfig

Which in its turn has a "payload" key, described as:
> APNs payload as a JSON object, including both aps dictionary and
custom payload. See [Payload Key
Reference](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification).
If present, it overrides
[google.firebase.fcm.v1.Notification.title](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Notification.FIELDS.title)
and
[google.firebase.fcm.v1.Notification.body](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Notification.FIELDS.body).

The Payload Key Reference contains information on supported keys,
including a "badge":
> The number to display in a badge on your app’s icon. Specify 0 to
remove the current badge, if any.
Had some struggles setting up FCM in my repository with the given
documentation, so I'm suggesting some extra steps people might look over
on their initial tests. From the documentation it's not immediately
clear that `data` is not where the title and body of a notification go.
Leaving `notification` blank causes rpush to return a success, but
nothing will actually arrive on the device. The project ID also might
have changed labels overtime, as setting the "project ID" as my
`firebase_project_id` instead of the "project number" also prevents
notifications from showing up.
Pending rpush#713. Necessary to avoid
blocking critical work like rpush#706.
Only difference was in the comment (possibly typo?)

Co-authored-by: Ben Langfeld <blangfeld@powerhrg.com>
Fixes rpush#692 

This should help release the gem on GitHub. When release, you can
dispatch the release workflow on GitHub Actions. Choose "Release"
workflow, and run it. The workflow will:
1. Build and push the gem with the version defined in
`rpush/version.rb`.
2. Tag the repository with `v<version>`. e.g., `v9.2.0`.
3. GitHub will create a release with release notes.

Because the release workflow uses an action called
`rubygems/release-gem`, to publish the gem via GitHub, the gem owners
must configure the ["Trusted
publishing"](https://guides.rubygems.org/trusted-publishing/). You must
[add GitHub as a trusted
platform](https://guides.rubygems.org/trusted-publishing/adding-a-publisher/).


------

Other changes:
1. I also bumped the Ruby in the development environment to 3.2 with
gems including `pg`, `mysql2` to resolve a few build problems on my
development environment.
2. I also use Gemfile for development dependencies as Rubocop suggests.
3. I updated `tests.yml` to run the latest Ubuntu. This can be further
improved to use GitHub repo's ruleset to set certain or all tests must
pass for PR to be able to merge. Happy to help on this area too.

---------

Co-authored-by: Ben Langfeld <blangfeld@powerhrg.com>
rstojano and others added 2 commits August 21, 2026 13:20
… structured push logging (#7)

* Retry APNs (apnsp8) frames dropped by a mid-flight connection reset

When the APNs HTTP/2 connection is reset while a batch is in flight,
net-http2 tears down the stream set and delivers the error to the
client's on(:error) callback; #join then returns normally. The in-flight
notifications never receive an on(:close), so they are marked neither
delivered, failed, nor retryable, and the batch discards them silently on
completion -- no retry, no failure record. An idle apnsp8 socket (e.g. a
low-volume Live Activity app) that APNs resets loses the next frame this
way.

Fix, localized to the apnsp8 transport plus a read-only Batch helper:

- Batch#unresolved returns notifications with no terminal outcome.
- Apnsp8::Delivery#perform reconciles after #join: any unresolved
  notification is re-queued (retryable) instead of dropped. No-op on the
  normal path where every stream reported a result.
- handle_response treats an absent status code (stream closed before APNs
  answered) as a transport failure -> retry, not a permanent failure.
- Errno::ECONNRESET added to the retryable rescue (matching apns2), in
  case a reset ever surfaces synchronously rather than via on(:error).

Every existing APNs status outcome (200/410/400/429/500/503) is
unchanged; only the "no verdict from APNs" cases move from
{silent drop, permanent fail} to {retry}.

Specs: Batch#unresolved, plus apnsp8 reconciliation and no-status handling.

* Structured push-pipeline logging (apnsp8 transport + Loggable helper)

Add Loggable#log_push_event, which emits logfmt-style key=value lines so
Datadog indexes each field and the lines join to the nexus push logs on
rpush_notification_id. Give every apnsp8 notification a discoverable
outcome:

- delivery: delivered / failed / retrying (with the device token truncated),
  and a retryable APNs response is logged as a retry, not a failure
- app_runner: enqueued per notification (both the batch and non-batch
  paths), and startup_failed when an app cannot start (bad cert)
- dispatcher: connection_error on a socket-level client error, so a
  mid-flight reset is findable

Logging only; no delivery behavior changes.

* Quote the app name in structured push logs

The app name bypassed push_event_value, so a name containing whitespace,
=, or " produced an unparseable logfmt line and broke Datadog field
indexing. Route it through the same quoter as every other field.

* Log the error message on connection_error, and keep push logs single-line

Review follow-up: the connection_error line logged only the exception class,
so two same-class socket errors with different messages were indistinguishable
in a Datadog query. Include the message. Also fold newlines in structured
field values so a multi-line message cannot split the one-line record.
Apns2::Delivery has the same latent silent-drop pattern PR #7 fixed for
Apnsp8, called out there as a deliberate follow-up: a dropped HTTP/2
connection tears down its in-flight streams via net-http2's on(:error)
callback rather than raising into #perform, so those notifications never
receive an on(:close) and are marked neither delivered, failed, nor
retryable -- silently discarded when the batch completes.

This is the transport a cert-based app (e.g. currypizzahouse_ios) uses,
observed in production as a connection that goes completely silent for
hours -- no sends, no errors logged -- then resumes on its own with no
restart. Reused a single push message's rpush_notifications rows confirm
the same request succeeding on one attempt and silently vanishing (no
delivered/failed/retryable outcome) on another, minutes apart, against
the same two device tokens.

Fix, mirroring Apnsp8::Delivery#perform and reusing Batch#unresolved
(already added by #7, transport-agnostic):

- Apns2::Delivery#perform reconciles after #join: any unresolved
  notification is re-queued (retryable) instead of dropped.
- handle_response treats an absent status code (stream closed before
  APNs answered) as a transport failure -> retry, not a permanent
  failure (previously this fell through to the `else` branch and was
  marked permanently *failed* -- worse than Apnsp8's pre-#7 silent drop).
- Also fixes the untested per-notification SSLError rescue named in #7's
  "Follow-ups" section: preparing a request could raise before the
  notification ever got a stream, and the old code just logged and moved
  on, leaving it with no outcome at all. Now retried via the same path.

Every existing APNs status outcome (200/410/400/429/500/503) is
unchanged; only the "no verdict from APNs" cases move from
{silent drop, permanent fail} to {retry} -- strictly safer, matching #7's
regression-safety argument for Apnsp8.

Also brings structured push-event logging (#7) to this transport for
parity: delivered/failed/retrying events on Delivery, and the dispatcher's
connection_error now includes the error message (not just its class) --
addressing the one open review comment on #7 before it repeats here.

Tests mirror #7's apnsp8 coverage: the reconnection sweep, no-status
handling, the SSLError-at-prepare-time path, and the logging format
cases.

Stacked on rstojano/apnsp8-retry-dropped-frames (#7) to reuse
Batch#unresolved and Loggable#log_push_event without redefining them;
rebase onto master once #7 merges.

Co-authored-by: Robert Stojanovski <robert@thanx.com>
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0aba80d5-a42e-4d44-99f4-3268e2a308ac


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Resolve conflicts using the ours strategy. master (74f7afc) is the
pre-rebase fork state; every change on it is the same custom work this
branch already contains, replayed on top of upstream rpush v9.2.0. The
only unique change on master is "Allow Rails 5.1", intentionally dropped
as obsolete under v9.2.0's activesupport >= 6.0 constraint.
@rstojano rstojano closed this Aug 21, 2026
@rstojano
rstojano deleted the rstojano/rpush-fork-diff branch August 21, 2026 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants