Skip to content

Heartbeat refresher for chains nearing refresh-token expiry - #3

Open
helording wants to merge 19 commits into
NexPB:masterfrom
helording:keepalive-refresher
Open

Heartbeat refresher for chains nearing refresh-token expiry#3
helording wants to merge 19 commits into
NexPB:masterfrom
helording:keepalive-refresher

Conversation

@helording

Copy link
Copy Markdown
Contributor

On-demand refreshing only fires when a token is used, so a dormant shop can silently cross the 90-day refresh-token expiry. This adds a supervision-tree GenServer that proactively rotates chains before they reach the cliff.

Changes

  • refresh_token_window option on the store's refresh_token/2, so a refresh can be triggered for chains whose refresh token expires within a window (not just expired access tokens).
  • ExShopifyApp.AccessToken.KeepAlive GenServer: scans every :interval (default 6h) for chains whose refresh token expires within :window (default 7 days) and refreshes them through the store's lock-serialized path. Multi-node safe — the row lock plus in-lock re-check collapse concurrent ticks into a single Shopify call. Lifetime (non-expiring) rows are never touched.
  • README documents adding the process to a supervision tree.

Notes

Split out of #2, which now only contains the AccessToken.Migrations helper. Independent of that PR — branches off master.

🤖 Generated with Claude Code

helording and others added 4 commits June 15, 2026 10:54
…ken expiry

A dormant shop's access token may sit fresh or untouched while its refresh
token approaches the 90-day cliff, after which only the merchant can restore
access. The new option forces a locked refresh when the refresh token expires
inside the window, with the same lock-and-recheck dedup and stale-fallback
semantics as the soft window. Groundwork for the KeepAlive process.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A supervision-tree GenServer that scans for chains whose refresh token
expires inside a window and rotates them via the store's lock-serialized
refresh_token/2 (refresh_token_window option). Multi-node safe: the row lock
plus in-lock re-check collapse concurrent ticks into one Shopify call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread config/test.exs Outdated
Comment thread test/support/repo_case.ex
Comment thread test/ex_shopify_app/access_token/lock_timeout_test.exs
@helording
helording marked this pull request as ready for review June 16, 2026 00:17
@helording

This comment was marked as outdated.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds proactive “keep-alive” refreshing for Shopify offline token chains that are approaching refresh-token expiry, so dormant shops don’t silently cross the 90‑day cliff. This is implemented by introducing a refresh-token-expiry window into the locked refresh decision path and a periodic heartbeat process that scans for soon-to-expire chains and rotates them.

Changes:

  • Add :refresh_token_window option to the locked refresh decision so refresh can be forced when the refresh token is near expiry.
  • Introduce ExShopifyApp.AccessToken.Heartbeat GenServer to periodically scan and rotate expiring chains.
  • Rework tests to use a Mox-backed Tesla adapter and SQL Sandbox (plus new test helpers/case template).

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/test_helper.exs Defines Mox Tesla adapter mock, enables log capture, and sets SQL sandbox mode.
test/support/repo_case.ex Adds a shared-mode sandbox + Mox-global test case template for repo/heartbeat tests.
test/support/factory.ex Introduces ExMachina factories/fixtures for access-token rows and mock responses.
test/ex_shopify_app/access_token/token_test.exs Updates token expiry tests and adds refresh_token_expiring?/3 coverage.
test/ex_shopify_app/access_token/repo_test.exs Refactors repo tests to RepoCase + Mox adapter expectations; adds :refresh_token_window tests.
test/ex_shopify_app/access_token/lock_timeout_test.exs Adds a dedicated real-connection test for the lock_timeout behavior.
test/ex_shopify_app/access_token/heartbeat_test.exs Adds tests for the new Heartbeat tick behavior.
test/ex_shopify_app/access_token_test.exs Switches HTTP mocking from Tesla.Mock to Mox adapter expectations.
README.md Documents adding the heartbeat process to a supervision tree.
mix.exs Adds :mox and :ex_machina as test dependencies.
mix.lock Locks new test deps (mox, ex_machina, nimble_ownership).
lib/ex_shopify_app/access_token/token.ex Adds refresh_token_expiring?/3 helper (used to force refresh near refresh-token expiry).
lib/ex_shopify_app/access_token/repo/options.ex Adds option reader for :refresh_token_window.
lib/ex_shopify_app/access_token/repo.ex Integrates refresh-token-expiry window into the refresh decision (refresh_needed?/3).
lib/ex_shopify_app/access_token/heartbeat.ex New GenServer that periodically scans and refreshes expiring chains.
config/test.exs Routes Tesla through the Mox-backed adapter and enables Sandbox pool for TestRepo.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/ex_shopify_app/access_token/token.ex Outdated
Comment thread test/support/repo_case.ex
Comment thread lib/ex_shopify_app/access_token/heartbeat.ex Outdated
Comment thread test/ex_shopify_app/access_token/repo_test.exs Outdated
@helording

Copy link
Copy Markdown
Contributor Author

@NexPB copilot comments resolved

Comment thread lib/ex_shopify_app/access_token/repo.ex Outdated
Comment thread lib/ex_shopify_app/access_token/heartbeat.ex Outdated
helording and others added 5 commits June 18, 2026 11:25
Resolve conflicts in mix.exs and repo_test.exs, and adapt master's
migrate/2 tests and migrations test to the new Mox + ex_machina fixture
structure introduced on this branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve conflicts in mix.exs and test/ex_shopify_app/access_token/repo_test.exs.
Adapt master's migrate/2 and migrations tests to this branch's new Mox +
ex_machina fixture structure (replacing the Agent counter / Tesla.Mock setup).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@helording helording changed the title KeepAlive refresher for chains nearing refresh-token expiry Heartbeat refresher for chains nearing refresh-token expiry Jun 18, 2026
@helording
helording requested a review from NexPB June 18, 2026 04:00
Comment thread lib/ex_shopify_app/access_token/repo.ex Outdated
Comment thread test/support/factory.ex Outdated
Comment thread test/ex_shopify_app/access_token/heartbeat_test.exs Outdated
Comment thread lib/ex_shopify_app/access_token/heartbeat.ex Outdated
Comment thread lib/ex_shopify_app/access_token/heartbeat.ex
@helording
helording requested a review from NexPB June 19, 2026 01:45
Comment thread lib/ex_shopify_app/access_token/heartbeat.ex Outdated

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing tests for the pagination

@helording
helording force-pushed the keepalive-refresher branch 2 times, most recently from 58b89a6 to 110405d Compare June 25, 2026 04:12

@NexPB NexPB left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder: think about global setting of the heartbeat server

@helording
helording requested a review from NexPB July 7, 2026 06:00
Comment thread test/support/repo_case.ex
import ExShopifyApp.Factory
import ExShopifyApp.TestHelpers, only: [stored: 1]

import ExShopifyApp.HTTPMockHelpers,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] repo case importing http stubs be default feels a little off

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.

3 participants