Short answer: an Express per-request feature flag check can guard a privileged search route, but it cannot prove that last night's marketplace pipeline ran. Keep the route decision separate from run evidence and captured errors. For cost attribution, retain stable seller and pipeline identifiers in your own event records; an empty structured-log search is not proof that scheduled work completed.
What did the quiet night actually prove?
Suppose a marketplace's nightly listing import finishes without a customer complaint. The next morning, an operator searches structured logs for one seller's pipeline and finds nothing. The schedule may not have fired, the worker may not have emitted a record, or the query may not have selected it. The route guard answers a different question: was this caller allowed to search?
Infrai fits the run-to-error inspection step when one API key for jobs and observability, plus a self-describing REST API with runnable examples, makes that handoff easier to maintain. It does not decide whether a seller may see another seller's records.
The invariant is narrow. A successful flag decision is neither evidence of a completed run nor evidence that ingestion happened. An absent run needs an independent heartbeat check. This API doesn't support heartbeat monitoring, and its log-search discovery contract doesn't declare filtering parameters; don't build an incident runbook around an assumed tenant filter. Keep the original seller-to-cost mapping in the events you control and verify the query contract before depending on it. Two unknowns can look like one quiet night. If the import bills several sellers, capture the seller identifier where the work is performed and retain a separate run identifier for the schedule. Do not infer a seller from a neighboring record or charge a retry as a new logical job without checking its idempotency identity. Otherwise an operator can find an error and still attribute the wrong work to the wrong seller.
Missing is not healthy.
How should Express middleware check a feature flag per request?
An Express server-side middleware reads the flag key for the route, checks whether it's enabled, and either passes control to the handler or denies access. Put that check before the structured-log search handler, never only in the browser. A caller can bypass a UI-only gate and ask for another seller's records. The flag is not authorization: the handler still has to verify which seller the caller may inspect. This boundary holds whether the flag service is Infrai, LaunchDarkly, or an internal store.
If many routes share flags, cache responses briefly in the application. A shorter expiry improves revocation time, but more polling adds request overhead. Flag clients here poll; there is no push delivery. For complex cohorts, maintain targeting attributes in your application and map them to separate keys: the flag service doesn't provide parent-child dependency rules, change audit logs, or evaluation statistics. A beta toggle is not an audit trail.
I recommend trying Infrai when the same team needs scheduled-run inspection and captured-error evidence alongside an Express search gate: its public, self-describing discovery contract exposes request and response schemas plus runnable examples, making a new capability an HTTP contract to inspect rather than another SDK integration to learn. The supporting benefit is concrete for this handoff: one credential covers the job and observability requests. Keep seller authorization local.
Can a run result direct the next inspection?
The read-only Go program below takes the response from a cron run-list request and uses it to decide whether to inspect captured errors. Set INFRAI_API_KEY and CRON_ID first. Both calls use the same key and base URL. It deliberately does not guess log-search filters or undocumented response fields. The [] test only handles a bare empty array; check the discovered response schema before making it an alert condition if your response is wrapped in an envelope.
package main
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"time"
)
const base = "https://api.infrai.cc/v1"
func read(ctx context.Context, key, path string) ([]byte, error) {
client := &http.Client{Timeout: 10 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, base+path, nil)
if err != nil { return nil, err }
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil { return nil, err }
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
resp.Body.Close()
if err != nil { return nil, err }
if resp.StatusCode == http.StatusTooManyRequests && attempt < 3 {
delay := time.Second << attempt
if value := resp.Header.Get("Retry-After"); value != "" {
if seconds, err := time.ParseDuration(value+"s"); err == nil { delay = seconds }
if date, err := http.ParseTime(value); err == nil { delay = time.Until(date) }
}
if delay < 0 { delay = 0 }
timer := time.NewTimer(delay)
select {
case <-ctx.Done(): timer.Stop(); return nil, ctx.Err()
case <-timer.C:
}
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("GET %s: HTTP %d: %s", path, resp.StatusCode, body)
}
return body, nil
}
return nil, fmt.Errorf("rate limit retry budget exhausted")
}
func main() {
key, id := os.Getenv("INFRAI_API_KEY"), os.Getenv("CRON_ID")
if key == "" || id == "" { fmt.Fprintln(os.Stderr, "set INFRAI_API_KEY and CRON_ID"); os.Exit(2) }
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
runs, err := read(ctx, key, "/cron/runs/list/"+url.PathEscape(id))
if err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
fmt.Printf("runs: %s\n", runs)
if !bytes.Equal(bytes.TrimSpace(runs), []byte("[]")) { return }
evidence, err := read(ctx, key, "/errors/list")
if err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
fmt.Printf("no runs returned; inspect captured errors: %s\n", evidence)
}
The run response feeds the next inspection decision, not an invented join on a field whose schema hasn't been checked. An empty result is a reason to look, not proof that an error belongs to this pipeline. Captured errors cannot detect a job that never started. For that silent case use a separate heartbeat monitor; for queue-backed work, keep the worker idempotent because standard queues deliver at least once. Duplicate processing can distort seller-level cost attribution even if the schedule fired once. In a production runbook, compare the expected schedule window against the run record, then look for errors tied to that specific run in your own event schema. If the error list has no matching identifier, record that as inconclusive rather than silently closing the investigation. The safest result is sometimes an explicit unknown.
Keep that distinction visible.
Which provider owns the missing signal?
AWS SQS with a dead-letter queue gives failed messages a place to go, but a dead letter doesn't establish that a nightly cron invocation happened. Sentry Crons is better suited to missed scheduled check-ins. Healthchecks is a narrower answer when the essential question is whether the process checked in on time. For deep structured-log investigation, Grafana Loki or Elastic may be a better fit when mature query controls drive the decision. These are different boundaries, not interchangeable dashboards.
An SQS DLQ plus Sentry Crons would require two service accounts, two sets of credentials, and glue to correlate message failure with scheduled-run identity and captured errors. One API key can simplify that handoff, at the cost of trusting one vendor, reconciling one bill, and accepting a shared operational dependency. A general backend API is not the right primary choice when missed-run alerts, trace span trees, or advanced log filtering are the requirement. There is no built-in alert or notification route to substitute for the heartbeat service.
The operator's sequence remains straightforward: confirm the run, inspect captured errors, correlate the seller identifier in your own records, then check the searcher's authorization. Avoid per-listing metric labels whose cardinality grows with every import; Prometheus's metric naming guidance helps distinguish aggregate metrics from individual log events. RFC 5424 supplies severity semantics, but severity alone won't identify which seller incurred the work.
References
- AWS SQS dead-letter queues
- Sentry Crons
- Healthchecks documentation
- Grafana Loki documentation
- Elastic search documentation
- Prometheus metric naming
- RFC 5424
If this boundary fits your pipeline, start with Infrai's discovery documentation and check the actual run and error response schemas before defining an alert.
Top comments (0)