SefazNfe.DistDFe.Poller (sefaz_nfe v0.1.0)

Copy Markdown View Source

One process per tax ID (CNPJ or CPF) for NFeDistribuicaoDFe.

The library does not persist ult_nsu — the host does (AD-002). This process owns the poll loop, a process label for Observer, and the hand-off of each page to the caller's :handler.

Handler contract

:handler is required: a poller with nowhere to deliver is a no-op. It is a one-arity function or an mfa/0 (the page is prepended to the extra args), invoked in the poller process with a SefazNfe.DistDFe.t/0 page. Its return value owns the cursor:

  • {:ok, ult_nsu} — continue from the cursor the host committed
  • :ok — continue from page.ult_nsu as returned by SEFAZ
  • :stop — stop the poller normally (the child is :transient, so the supervisor leaves it down)

Delivery is at-least-once: the handler runs before the cursor moves, so a handler that raises crashes the poller and the supervisor restarts it at the cursor last given to start_link/1. DistDFe documents are idempotent by NSU, so a replay is cheaper than a silent gap.

Scheduling

The child is :transient, not the :permanent default: a handler answering :stop exits :normal and must stay down, while a crash still restarts at the last cursor.

Three things shape the next delay. A page whose cursor still trails maxNSU has a backlog — DistDFe answers around 50 documents per call — so it drains after @catch_up instead of a whole interval. A page carrying cStat 656 (consumo indevido) backs off for an hour, because the AN blocks the tax ID for that long and retrying sooner only renews the block. Every delay then gets up to 10% of jitter, always later and never earlier, so that thousands of pollers started by one release boot do not hit the AN in lockstep.

A transport error keeps the cursor and simply waits for the next tick; the library never retries DistDFe in a tight loop.

Logging

A multi-tenant host polls third-party tax IDs and DistDFe accepts CPF, so a raw identifier in a log line is personal data piling up (LGPD). Failures name the poller by the last four characters only, which is enough to tell two apart during an incident.

Options

  • :tax_id (required) — CNPJ (14) or CPF (11) of the interested party
  • :uf (required) — the querying party's own state, sent as cUFAutor
  • :cert (required) — SefazNfe.Certificate.t/0
  • :handler (required) — see above
  • :environment:homologation (default) or :production
  • :ult_nsu — starting cursor, default "0"
  • :intervalDuration.t/0 between polls, default 5 minutes
  • :fetch — one-arity fetch function, default &SefazNfe.dist_dfe/1. The seam that keeps mix test off the network, and the hook for a host that wraps the call in its own circuit breaker.

Summary

Functions

Returns a specification to start this module under a supervisor.

Types

handler()

@type handler() :: (page() -> {:ok, String.t()} | :ok | :stop) | mfa()

page()

@type page() :: SefazNfe.DistDFe.t()

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

start_link(opts)

@spec start_link(keyword()) :: GenServer.on_start()

via(tax_id)

@spec via(String.t()) :: {:via, module(), term()}