# `PhoenixKit.Modules.Emails.SQSPollingManager`
[🔗](https://github.com/BeamLabEU/phoenix_kit_emails/blob/0.4.2/lib/phoenix_kit/modules/emails/sqs_polling_manager.ex#L1)

Manager module for SQS polling via Oban jobs.

This module provides a unified API for managing SQS polling that can be
enabled/disabled dynamically without application restart.

## Features

- **Enable/Disable Polling**: Start or stop polling without restart
- **Manual Triggering**: Force immediate polling when needed
- **Status Monitoring**: Get current polling status and job information
- **Settings Integration**: Automatically uses PhoenixKit Settings
- **Interval Control**: Dynamically adjust polling frequency

## Architecture

Instead of using a GenServer, this manager uses Oban jobs for polling:
- Each job polls SQS once and schedules the next job
- Jobs check settings before executing (dynamic control)
- No need to restart GenServer when settings change

## Usage

    # Enable polling
    iex> PhoenixKit.Modules.Emails.SQSPollingManager.enable_polling()
    {:ok, %Oban.Job{}}

    # Disable polling
    iex> PhoenixKit.Modules.Emails.SQSPollingManager.disable_polling()
    :ok

    # Check status
    iex> PhoenixKit.Modules.Emails.SQSPollingManager.status()
    %{
      enabled: true,
      interval_ms: 5000,
      pending_jobs: 1,
      last_run: ~U[2025-09-20 15:30:45Z],
      queue_url: "https://sqs.eu-north-1.amazonaws.com/..."
    }

    # Trigger immediate poll
    iex> PhoenixKit.Modules.Emails.SQSPollingManager.poll_now()
    {:ok, %Oban.Job{}}

    # Change polling interval
    iex> PhoenixKit.Modules.Emails.SQSPollingManager.set_polling_interval(3000)
    {:ok, %Setting{}}

## Integration

This manager is the single control surface for SQS polling: it drives the
Oban `SQSPollingJob`. `enable_polling/0` and `disable_polling/0` back the
admin UI toggle. `poll_now/0` and `set_polling_interval/1` are part of the
public consumer API (no admin-UI caller today); they are safe to call
directly from host applications.

# `disable_polling`

Disables SQS polling by updating the configuration.

Only `sqs_polling_enabled` is cleared. `email_ses_events` — which
`enable_polling/0` turns on — is deliberately left alone, and the
asymmetry is the point: enabling asserts "SES event tracking is a
thing on this install", which stays true while polling is paused, and
the same flag also gates the SNS webhook path
(`Emails.Web.WebhookController`), which has nothing to do with SQS
polling. Clearing it here would silently switch off webhook ingestion
from a button labelled "stop polling". It keeps its own control in the
Email Tracking page (`Web.EmailTracking`, not the settings section of
the same name) for an operator who really does mean
"no SES events at all". See `EventTracker`'s moduledoc, which spells
out why an eligibility flag otherwise must not move with an operator
toggle.

No explicit job cancellation: `SQSPollingJob.perform/1` checks
`should_poll?/0` before doing any work AND before self-scheduling its
next cycle (see that module). At most one already-queued job fires
once more, sees polling disabled, does nothing, and does not
re-schedule — the chain dies on its own within one cycle. That one
harmless no-op run is the accepted cost of not doing a manual DELETE
here.

## Returns

- `:ok` - Successfully disabled

## Examples

    iex> PhoenixKit.Modules.Emails.SQSPollingManager.disable_polling()
    :ok

# `enable_polling`

Enables SQS polling by setting the configuration and starting the first job.

Three writes — `email_ses_events`, `sqs_polling_enabled` and the first
Oban job — land in ONE transaction. They used to run as three bare
steps chained by `with`, so a failure at step two or three left
`email_ses_events` flipped on by a click that reported an error: the
operator saw "failed to enable", the install silently gained an
eligibility flag it never had, and nothing on the page said so.
`Settings.update_settings_batch/1` is the obvious tool and the wrong
one here — it writes `key`/`value` only, so on an install where these
rows do not exist yet it would create them without the `email_system`
module tag the settings page groups by.

Cache invalidation cuts both ways here. On a rollback it stays valid:
the writers only CLEAR entries, so the next read comes from the
rolled-back row rather than a stale cached one. On success it is not
enough on its own — the writers clear inside the transaction, before
the commit is visible, so a reader that misses the cache in that window
would cache the pre-commit value and keep it for the cache's whole TTL.
Both keys are therefore invalidated again after the commit.

## Returns

- `{:ok, job}` - Successfully enabled and started first job
- `{:error, reason}` - Nothing was written; both settings keep their prior values

## Examples

    iex> PhoenixKit.Modules.Emails.SQSPollingManager.enable_polling()
    {:ok, %Oban.Job{id: 1, queue: "sqs_polling"}}

# `poll_now`

Triggers an immediate polling job.

This creates a new job that will execute as soon as possible,
regardless of the normal polling schedule.

The job carries `args: %{"forced" => true}`, which
`SQSPollingJob.perform/1` honours by bypassing the
`sqs_polling_enabled` toggle for that single cycle (it still respects
the system switch, the SES-events switch, and the sender-aware gate).
Without it a manual poll while the toggle is off would insert a job
that runs, sees polling disabled, and silently does nothing. The
distinct args also keep this insert in its own uniqueness namespace,
so it never moves or cancels the regular chain's next scheduled tick
— see `insert_forced_poll_job/0`.

## Returns

- `{:ok, job}` - Successfully created immediate job
- `{:error, reason}` - Failed to create job

## Examples

    iex> PhoenixKit.Modules.Emails.SQSPollingManager.poll_now()
    {:ok, %Oban.Job{}}

# `set_polling_interval`

Sets the polling interval in milliseconds.

The new interval will be used for subsequent job scheduling.

## Parameters

- `interval_ms` - Interval in milliseconds (minimum 1000ms)

## Returns

- `{:ok, setting}` - Successfully updated
- `{:error, reason}` - Failed to update

## Examples

    iex> PhoenixKit.Modules.Emails.SQSPollingManager.set_polling_interval(3000)
    {:ok, %Setting{}}

# `status`

Returns the current status of SQS polling.

## Returns

A map with:
- `enabled` - Whether polling is enabled
- `interval_ms` - Current polling interval
- `pending_jobs` - Number of scheduled jobs
- `last_run` - Timestamp of last completed job (if any)
- `queue_url` - Configured SQS queue URL

## Examples

    iex> PhoenixKit.Modules.Emails.SQSPollingManager.status()
    %{
      enabled: true,
      interval_ms: 5000,
      pending_jobs: 1,
      last_run: ~U[2025-09-20 15:30:45Z],
      queue_url: "https://sqs.eu-north-1.amazonaws.com/..."
    }

---

*Consult [api-reference.md](api-reference.md) for complete listing*
