← Tillbaka

Teknisk dokumentation

Hur URL & 301 Checker processerar jobb, hanterar rate limiting och undviker att DDos:a målsajter — inklusive Cloudflare-skyddade webbplatser.

1. Jobbflöde — från CSV till resultat

Varje körning är ett asynkront bakgrundsjobb med en unik UUID-URL.

CSV-uppladdning
      │
      ▼
┌─────────────┐      urls.json   ┌──────────────┐
│  /api/upload │ ───────────────▶ │  jobStore.ts │  (disk + minnescache)
│  (POST)      │  meta.json       │  .jobs/[id]/ │
└─────────────┘  results.jsonl   └──────────────┘
      │                                  │
      │  fire-and-forget                 │
      ▼                                  ▼
┌──────────────┐            ┌────────────────────┐
│ processor.ts │            │  /api/jobs/[id]    │
│  (bakgrund)  │            │  (GET) polling     │
└──────────────┘            └────────────────────┘
      │
      ├── Grupperar URLs per hostname
      ├── Startar en worker per domän (parallellt)
      └── Varje worker: sekventiell, med delay
StegVad händerFil
UploadCSV parsas, URLs valideras, jobb skapas med UUIDapi/upload/route.ts
Skapa jobbSparar meta.json, urls.json, tom results.jsonllib/jobStore.ts
BakgrundsprocessStartas direkt (fire-and-forget), blockerar inte HTTP-svaretlib/processor.ts
Resultat-appendVarje kontrollerat URL appas till results.jsonl — O(1) per URLlib/jobStore.ts
PollingFrontend hämtar status + resultat var 2:a sekundapp/jobs/[id]/page.tsx

2. Domain-aware processing — en worker per domän

Det viktigaste skyddet mot att DDos:a en sajt: alla URLs till samma domän körs strikt sekventiellt.

Input-URLs grupperas per hostname:

  exempel.se  →  [url1, url2, url3, ...]   ─┐
  annan.se    →  [url4, url5, ...]           ├─▶ körs parallellt
  tredje.se   →  [url6, url7, ...]          ─┘

  Inom varje grupp:
  url1 ──(300 ms gap)──▶ url2 ──(300 ms gap)──▶ url3 ...
                                                        └─ sekventiell, aldrig parallell

Globalt semaphore: max 5 requests i luften totalt
(sprider sig automatiskt över alla domäner)
KonstantVärdeInnebär
GLOBAL_CONCURRENCY5Max 5 HTTP-requests i luften totalt, oavsett antal domäner
MAX_CONCURRENT_PER_DOMAIN1 (implicit)Varje domän-worker är sekventiell — aldrig parallella requests till samma host
MIN_DELAY_PER_DOMAIN_MS300 msMinst 300 ms mellan varje request till samma hostname ≈ max 3 req/s per domän

Om en CSV innehåller 1 000 URLs till samma domän tar det ~5 minuter (1 000 × 300 ms). Med URLs spridda över 5 domäner tar det ~1 minut — domänerna arbetar parallellt.

3. Cloudflare-anpassad rate limiting

Cloudflare:s bot-management triggar primärt på burst-trafik — hög hastighet under kort tid — snarare än låg, jämn belastning. Dessa regler håller oss under radarn.

ScenarioCF:s reaktionVår motåtgärd
Många parallella requests till samma IP/host429 Too Many RequestsMax 1 concurrent + 300 ms gap per domän
Bot-liknande beteende (ingen User-Agent, massa requests)403 / JS-challengeKonfigurerbar User-Agent — välj rätt kategori i UI:t
Hög belastning på origin bakom CF503 Service UnavailableExponentiell backoff + retry (se sektion 4)
Tillfällig CF-spik eller origin-problem503 / 429Automatisk retry upp till 3 gånger per URL

4. Exponentiell backoff vid 429 & 503

När en domän svarar med 429 (rate limited) eller 503 (överbelastad) pausar workern för den domänen och försöker igen — utan att blockera andra domäner.

Försök 1: request → 429
  └─▶ vänta 30 s → försök igen

Försök 2: request → 429
  └─▶ vänta 60 s → försök igen

Försök 3: request → 429
  └─▶ vänta 120 s → försök igen

Försök 4: request → 429
  └─▶ ge upp, spara resultatet som 429 och gå vidare till nästa URL
KonstantVärdeFörklaring
BACKOFF_BASE_MS30 000 ms (30 s)Väntetid vid första retry — Cloudflare:s Retry-After är ofta 30–60 s
BACKOFF_MAX_MS300 000 ms (5 min)Taket för en enskild vänteperiod — backoff dubblas men aldrig mer än 5 min
MAX_RETRIES3Maximalt 3 retries per URL — totalt 4 försök inklusive det första
Heartbeatvar 30 s under backoffUppdaterar lastActivityAt under lång väntan — förhindrar false-positive "Fastnad"-status

Backoff-sleep påverkar bara den aktuella domänen. Övriga domäner fortsätter köra normalt under hela väntetiden.

5. Pause / Resume / Cancel & Auto-recovery

Jobb kan pausas, återupptas och avbrytas live. Kontrollsignaler skrivs till disk så att de fungerar korrekt oavsett vilken server-instans som hanterar anropet.

ÅtgärdVad händer server-sideLämpligt när
⏸ PausaSkriver paused: true till control.json. Varje domain-worker pollar filen var 500 ms och slutar ta nya URLs. Pågående request slutförs.Du vill inspektera hittills-resultat, eller tillfälligt minska belastningen
▶ FortsättRensar paused-flaggan. Alla workers vaknar inom 500 ms och fortsätter från nästa URL i sin kö.Inspektionen är klar och du vill fortsätta körningen
✕ AvbrytSkriver cancelled: true till control.json. Workers avslutar efter nuvarande URL. Resultat hittills bevaras.Du vill inte slutföra körningen — resultat sparas ändå
↺ ÅterupptaLäser results.jsonl för att se vilka URLs som redan är klara, beräknar resterande och startar en ny process från rätt offset. Anropas automatiskt efter 15 s om jobbet är fastnat.Jobb fastnat vid serveromstart eller nätverksavbrott
Tillståndsmaskinen:

  pending ──▶ processing ──▶ done
                  │  ▲  ▲
                  ▼  │  │
               paused │  │
                  │   │  │
                  └───┘  │
                         │
               stuck ────┘  (härledd — auto-recover efter 15 s)
                  │
               cancelled

Kontroll-flaggorna lagras i control.json på disk — inte enbart i minnet. Det innebär att pause/cancel fungerar korrekt även när olika API-routes hanteras av separata Next.js-modulinstanser. Vid serveromstart visas jobb som "Fastnad" efter 3 minuters inaktivitet och återupptas automatiskt.

6. Stuck-detection — automatisk felidentifiering

Ett jobb som inte rapporterat aktivitet på länge markeras automatiskt som "Fastnad" utan att status behöver persisteras.

ParameterVärdeLogik
STUCK_THRESHOLD_MS180 000 ms (3 min)Om lastActivityAt är äldre än 3 min och status är processing returneras stuck — härledd, inte sparad
Heartbeat under backoffvar 30 sUppdaterar lastActivityAt under backoff-sleep så att 3-minutersgränsen aldrig nås vid normala retries
Paused / cancelledN/AStuck-detection aktiveras inte för dessa statusar — den kontrollerar bara status === "processing"

7. Lagring — filer och minnescache

Ingen databas krävs. All data lagras som filer på disk med en write-through cache i minnet.

FilFormatInnehåll
.jobs/[uuid]/meta.jsonJSONStatus, antal URLs, tidsstämplar, User-Agent — läses alltid från disk för korrekthet
.jobs/[uuid]/urls.jsonJSON arrayAlla inmatade URLs — läses en gång vid jobbstart
.jobs/[uuid]/results.jsonlJSONL (ett JSON-objekt per rad)Resultat appas med appendFileSync — O(1) per URL, ingen risk för race condition
.jobs/[uuid]/control.jsonJSONKontrollsignaler: paused och cancelled — skrivs och läses från disk för att fungera korrekt mellan Next.js-modulinstanser

getMeta läser alltid från disk (ej cache-first) för att garantera korrekt status oavsett vilken API-route som anropade. resultCache används däremot för snabb läsning av resultat under aktiv körning — cachad data skrivs om vid serveromstart.

URL & 301 Checker · Changelog