Skip to content

Autotopup invoice failed to be added to the registry #2948

Description

@vohmar

On 23.07 registrar's auto topup rule kicked in and billing system generated an invoice for them. Billing system shows that the invoice was created at 9:12 and e-invoice was sent 9:13, but in the e-invoicing system we see that the e-invoice was received a day later on 24th of July at 9:40 and immediately forwarded to registrar's bank for payment. The payment was received the same day (24.07). The invoice was never copied to registry and thus when payment was received new invoice was created.

It seems there was a significant delay somewhere on the billing side or in communication with registry creating this situation. From the logs we see that

Arve loodi, kuid Registry transaction rollbackis, sest Billingu /e_invoice/e_invoice endpointi request kestis 60.538 sekundit ja Registry Net::HTTP sai Net::ReadTimeout.

Billing oli selleks hetkeks 144677 juba enda baasi loonud ning e-arve saatmise job lõpetas edukalt umbes sekund pärast Registry timeouti. Kuna Billing on eraldi süsteem/DB, ei rollbackinud Billingu poolel tehtud muudatus koos Registry transactioniga.

Järgmine registrars:reload_balance cron kell 16:12 nägi Elkdata pending=false ning lõi uuesti arve 144678. Registry cron logist on näha, et see job lõpetas edukalt.

Kõrvalviga: kell 12:14:13 proovis Billing EInvoiceResponseSenderJob kaudu Registry poole tulemust tagasi saata, kuid sai vastuseks HTML-i ja üritas seda JSON-ina parsida:

JSON::ParserError
unexpected character: '<!DOCTYPE'
Seda tasub eraldi uurida. Võimalik, et Registry callback endpoint tagastas error page'i, kuid selle konkreetne põhjus pole veel callbacki koodist/logidest välja tulnud.

TL;DR: registrars:reload_balance hoidis Registry DB transactioni avatuna ajal, mil tehti sünkroonne HTTP päring Billingusse. Billing omakorda ootas sünkroonselt Omniva e-arve saatmise lõpuni ~60.5 sekundit. See ületas Registry Net::HTTP read timeouti, Registry rollbackis 144677, kuid Billingu eraldiseisev side-effect jäi alles.

Mr. AI suggested 3 changes:

1. Billing ei tohiks Omnivat HTTP requesti sees oodata
Praegu:

SendEInvoiceJob.perform_now(e_invoice_data)
render json: ...

Muuta asynciks:

SendEInvoiceJob.perform_later(e_invoice_data)
render json: { message: 'Invoice data received' }, status: :accepted

Siis:

Registry -> Billing POST
Billing -> enqueue job
Billing -> 202 kohe tagasi

hiljem:

Billing job -> Omniva

See üks muudatus oleks tõenäoliselt konkreetse 144677 intsidendi ära hoidnud.

2. Registry ei tohiks väliseid HTTP calle DB transactioni sees teha
See outer transaction:

Registrar.transaction do
  Registrar.all.each do |registrar|

on eriti halb, sest ühe registrari timeout võib rollbackida kogu jooksu.

Transaction peaks piirnema lokaalse state'i muutmisega. Näiteks kontseptuaalselt:

invoice = nil


Registrar.transaction do
  invoice = registrar.create_invoice!
  registrar.mark_reload_pending!
end

transaction on COMMITitud
BillingJob.perform_later(invoice.id)

Veel parem: kasutada transactional outbox / after_commit mustrit, et Billingu job enqueue toimuks alles pärast invoice commit'i.

3. Billing API peab olema idempotentne
See on oluline isegi pärast async fixi.

Kui Registry saadab kaks korda:

invoice_number=144677

peab Billing suutma öelda:

144677 already processed

ja tagastama olemasoleva tulemuse, mitte looma uut side-effecti.

Näiteks:

idempotency key = registry:invoice:144677

ja Billing DB-s UNIQUE constraint / processed-request kirje.

Sest Net::ReadTimeout ei tähenda, et request jäi serveris tegemata. See tähendab ainult, et klient ei saanud vastust õigel ajal.

Prioriteet

Kui arendajale panna konkreetne tegevusjärjekord:

Billing perform_now → perform_later ja endpoint vastab kohe.
Registry Billing API callid DB transactionist välja.
Idempotency invoice_number/request-id põhjal.
Lisa eksplitsiitsed open_timeout / read_timeout väärtused ja korralik retry policy.
Eemalda outer transaction üle Registrar.all.each; ühe registrari error ei tohiks kogu batchi rollbackida.
Vaata üle ka EInvoiceResponseSenderJob HTML→JSON error, mis tekkis pärast seda intsidenti.

Timeouti lihtsalt 60 → 120 sekundile tõstmine ei ole päris fix. See vähendaks esinemissagedust, aga sama consistency bug jääks alles — järgmine kord võib Omniva võtta 121 sekundit või ühendus katkeda täiesti.


Please analyse this situation and suggestions change/update the ticket as necessary. And if anything makes sense here then implement as best

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions