Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
341 lines
14 KiB
Protocol Buffer
341 lines
14 KiB
Protocol Buffer
syntax = "proto3";
|
|
|
|
package s3_lifecycle_pb;
|
|
|
|
option go_package = "github.com/seaweedfs/seaweedfs/weed/pb/s3_lifecycle_pb";
|
|
option java_package = "seaweedfs.client";
|
|
option java_outer_classname = "S3LifecycleProto";
|
|
|
|
// Storage layout reference (see S3_LIFECYCLE_REDESIGN.md):
|
|
//
|
|
// /etc/s3/lifecycle/<bucket>/<rule_hash_hex>/<action_kind>/state -> LifecycleState
|
|
// /etc/s3/lifecycle/<bucket>/<rule_hash_hex>/<action_kind>/pending -> repeated PendingItem
|
|
// /etc/s3/lifecycle/<bucket>/_bootstrap -> BootstrapState
|
|
// /etc/s3/lifecycle/_reader/reader_state -> ReaderState
|
|
// /etc/s3/lifecycle/_reader/blockers -> repeated BlockerRecord
|
|
// /etc/s3/lifecycle/_reader/retry_budget -> repeated RetryBudgetEntry
|
|
//
|
|
// State is per-(rule_hash, action_kind), not per-rule: a single XML <Rule>
|
|
// with N action sub-elements expands into N compiled actions, each with
|
|
// its own state file and its own pending file. Sibling actions of the
|
|
// same rule can degrade or activate independently.
|
|
|
|
// ActionKind classifies the lifecycle action a single compiled entry
|
|
// represents. A single XML rule may produce multiple compiled entries —
|
|
// one per populated action.
|
|
enum ActionKind {
|
|
ACTION_KIND_UNSPECIFIED = 0;
|
|
EXPIRATION_DAYS = 1; // Expiration.Days
|
|
EXPIRATION_DATE = 2; // Expiration.Date
|
|
NONCURRENT_DAYS = 3; // NoncurrentVersionExpiration.NoncurrentDays (with optional NewerNoncurrent retention)
|
|
NEWER_NONCURRENT = 4; // NoncurrentVersionExpiration.NewerNoncurrentVersions, count-only (no NoncurrentDays)
|
|
ABORT_MPU = 5; // AbortIncompleteMultipartUpload.DaysAfterInitiation
|
|
EXPIRED_DELETE_MARKER = 6; // Expiration.ExpiredObjectDeleteMarker
|
|
}
|
|
|
|
// SeaweedS3LifecycleInternal is the worker-to-S3 service that performs the
|
|
// actual lifecycle deletion. The lifecycle worker computes the (rule, action)
|
|
// verdict locally; the S3 server is the only component allowed to mutate the
|
|
// filer state, so it gets the final word: it re-fetches the live entry,
|
|
// verifies the EntryIdentity CAS, runs object-lock protections, and dispatches
|
|
// to the appropriate internal helper.
|
|
service SeaweedS3LifecycleInternal {
|
|
rpc LifecycleDelete(LifecycleDeleteRequest) returns (LifecycleDeleteResponse);
|
|
}
|
|
|
|
// LifecycleDeleteOutcome captures every per-event verdict the worker needs.
|
|
// Cursor advance / pending mutation rules:
|
|
//
|
|
// DONE -> advance cursor / drop pending
|
|
// NOOP_RESOLVED -> advance cursor / drop pending (object already gone or stale identity)
|
|
// SKIPPED_OBJECT_LOCK -> log + counter; advance cursor (per design: object lock is operator concern)
|
|
// RETRY_LATER -> hold cursor; feed retry-budget; next batch retries from same position
|
|
// BLOCKED -> hold cursor; durable BlockerRecord written; operator must intervene
|
|
enum LifecycleDeleteOutcome {
|
|
LIFECYCLE_DELETE_OUTCOME_UNSPECIFIED = 0;
|
|
DONE = 1;
|
|
NOOP_RESOLVED = 2;
|
|
SKIPPED_OBJECT_LOCK = 3;
|
|
RETRY_LATER = 4;
|
|
BLOCKED = 5;
|
|
}
|
|
|
|
message LifecycleDeleteRequest {
|
|
// Routing.
|
|
string bucket = 1;
|
|
string object_path = 2; // bucket-relative; no leading slash
|
|
string version_id = 3; // empty for non-versioned
|
|
bytes rule_hash = 4; // 8 bytes; matches the rule's per-rule dir
|
|
ActionKind action_kind = 5; // chooses dispatch within the rule
|
|
|
|
// Stream context (echoed in BlockerRecord on FATAL outcomes so operators
|
|
// can resolve the right (shard, delay) tuple).
|
|
string stream_shard = 10;
|
|
int64 stream_delay_seconds = 11;
|
|
int64 stream_position_ts_ns = 12;
|
|
int64 stream_position_offset = 13;
|
|
|
|
// CAS witness. The server re-fetches the live entry and aborts as
|
|
// NOOP_RESOLVED (STALE_IDENTITY) if any field doesn't match.
|
|
EntryIdentity expected_identity = 20;
|
|
|
|
// Snapshot id at the time the worker computed this verdict; the server
|
|
// verifies the (rule_hash, action_kind) is still in the current policy
|
|
// snapshot and aborts as NOOP_RESOLVED (STALE_POLICY) if not.
|
|
uint64 engine_snapshot_id = 21;
|
|
}
|
|
|
|
message LifecycleDeleteResponse {
|
|
LifecycleDeleteOutcome outcome = 1;
|
|
// Human-readable cause, e.g. "STALE_IDENTITY: mtime drift",
|
|
// "FATAL_EVENT_ERROR: malformed entry", or "TRANSPORT_ERROR: filer
|
|
// unavailable". Echoed into BlockerRecord.last_error on FATAL.
|
|
string reason = 2;
|
|
}
|
|
|
|
// MessagePosition mirrors weed/util/log_buffer.MessagePosition for durable
|
|
// storage. ts_ns + offset uniquely identifies a position within a per-filer
|
|
// log buffer; per-shard cursors (see ReaderState) use this shape to skip past
|
|
// already-processed events when replaying.
|
|
message MessagePosition {
|
|
int64 ts_ns = 1;
|
|
int64 offset = 2;
|
|
}
|
|
|
|
// Cursor map keyed by filer_id. Each delay group's cursor (ReaderState) and
|
|
// the predicate cursor (ReaderState) are per-filer-shard maps because
|
|
// LogEntry.Offset is per-filer and not globally unique.
|
|
message FilerShardCursor {
|
|
string filer_id = 1;
|
|
MessagePosition position = 2;
|
|
}
|
|
|
|
// EntryIdentity is the CAS witness used by LifecycleDelete to detect that
|
|
// the live entry hasn't changed between worker decision and server execution.
|
|
// Mismatch -> STALE_IDENTITY -> NOOP_RESOLVED.
|
|
message EntryIdentity {
|
|
int64 mtime_ns = 1;
|
|
int64 size = 2;
|
|
string head_fid = 3;
|
|
bytes extended_hash = 4; // sha256 over sorted Extended map; cheap and stable
|
|
}
|
|
|
|
// LifecycleState captures the durable per-action scheduling state. Keyed by
|
|
// (rule_hash, action_kind); persisted at
|
|
// /etc/s3/lifecycle/<bucket>/<rule_hash_hex>/<action_kind>/state
|
|
//
|
|
// A single XML <Rule> with N populated action sub-elements expands into N
|
|
// LifecycleState records, one per sibling action.
|
|
message LifecycleState {
|
|
// Proto3 best practice: zero value is an UNSPECIFIED sentinel so that
|
|
// legacy / partially-populated payloads don't silently default to a
|
|
// semantically active value. Persisted state files always set explicit
|
|
// non-zero values; reading UNSPECIFIED indicates corruption or a missing
|
|
// field.
|
|
enum RuleMode {
|
|
RULE_MODE_UNSPECIFIED = 0;
|
|
EVENT_DRIVEN = 1;
|
|
SCAN_AT_DATE = 2;
|
|
SCAN_ONLY = 3;
|
|
DISABLED = 4;
|
|
PENDING_BOOTSTRAP = 5;
|
|
}
|
|
// DEGRADED_REASON_UNSPECIFIED replaces the old NONE sentinel: an unset
|
|
// / zero degraded_reason means "no active degradation" — operators
|
|
// treat UNSPECIFIED as healthy.
|
|
enum DegradedReason {
|
|
DEGRADED_REASON_UNSPECIFIED = 0;
|
|
LAG_HIGH = 1;
|
|
PENDING_FULL = 2;
|
|
DELETE_FAILURES = 3;
|
|
OPERATOR_PAUSED = 4;
|
|
RETENTION_BELOW_HORIZON = 5;
|
|
LOST_LOG = 6;
|
|
}
|
|
|
|
bytes rule_hash = 1; // 8 bytes; matches the parent rule dir
|
|
ActionKind action_kind = 2; // matches the leaf dir name
|
|
string rule_id = 3; // display only; identical across sibling actions
|
|
RuleMode mode = 4;
|
|
DegradedReason degraded_reason = 5;
|
|
int64 degraded_since_ns = 6;
|
|
bool bootstrap_complete = 7;
|
|
int64 bootstrap_started_at_ns = 8;
|
|
int64 bootstrap_completed_at_ns = 9;
|
|
|
|
// Safety-scan scheduling. Set by the safety-scan job when a SCAN_ONLY
|
|
// (or periodic safety-scan) bootstrap pass completes; consulted by the
|
|
// detector to decide whether to emit the next bootstrap task.
|
|
int64 last_safety_scan_ts_ns = 10;
|
|
int64 next_safety_scan_ts_ns = 11;
|
|
|
|
// Counters for observability; no behavior depends on these.
|
|
// - evaluated_total: live entries the worker considered for this action
|
|
// (filter-matched + due-checked); includes objects that ended up not
|
|
// yet eligible.
|
|
// - expired_total: successful DONE outcomes (object/version/marker/MPU
|
|
// removed) under this action.
|
|
// - metadata_only_total: subset of expired_total where the worker
|
|
// short-circuited via volume-TTL routing (chunks left to volume GC).
|
|
// - error_total: outcomes that did NOT advance the cursor under this
|
|
// action (RETRY_LATER, BLOCKED, FATAL_EVENT_ERROR).
|
|
int64 evaluated_total = 12;
|
|
int64 expired_total = 13;
|
|
int64 metadata_only_total = 14;
|
|
int64 error_total = 15;
|
|
}
|
|
|
|
// PendingItem records a not-yet-due eligibility for late-predicate exceptions
|
|
// (e.g. tag added at age 30d on a 60d rule). Drained by s3.lifecycle.drain.
|
|
// One use only — not the steady-state path.
|
|
message PendingItem {
|
|
string object_path = 1;
|
|
string version_id = 2;
|
|
int64 due_at_ns = 3;
|
|
EntryIdentity expected_identity = 4;
|
|
}
|
|
|
|
// BootstrapState tracks the bucket walker's resume point. Operator-resolution
|
|
// of a BOOTSTRAP-stream blocker does NOT mutate this field — the next
|
|
// scheduled bootstrap task picks up from last_scanned_path and re-walks.
|
|
message BootstrapState {
|
|
string last_scanned_path = 1;
|
|
int64 started_at_ns = 2;
|
|
int64 completed_at_ns = 3; // zero while in progress
|
|
}
|
|
|
|
// ReaderState is the cluster-singleton reader's durable cursors. One file at
|
|
// /etc/s3/lifecycle/_reader/reader_state, written by the singleton task.
|
|
message ReaderState {
|
|
string primary_filer_endpoint = 1;
|
|
|
|
// Per-delay-group, per-filer-shard cursors. Outer key is delay_seconds.
|
|
// Inner FilerShardCursor list maps filer_id -> MessagePosition.
|
|
map<int64, FilerShardCursorList> last_processed_original = 2;
|
|
|
|
// Predicate-change pass cursor, keyed by filer_id.
|
|
repeated FilerShardCursor last_processed_predicate = 3;
|
|
|
|
// Filer-shard keys whose cursor reached range.latest and were safely GC'd.
|
|
// Used by the lost-log gate at task entry to distinguish "F was tail-drained
|
|
// safely then pruned" from "F's logs vanished before catch-up". Encoded as
|
|
// stream-specific keys (same shape as RetryBudgetEntry.StreamKey).
|
|
repeated TailDrainedKey tail_drained_streams = 4;
|
|
|
|
int64 last_checkpoint_ns = 5;
|
|
}
|
|
|
|
message FilerShardCursorList {
|
|
repeated FilerShardCursor cursors = 1;
|
|
}
|
|
|
|
// TailDrainedKey identifies a (stream_kind, shard, [delay]) tuple whose
|
|
// cursor previously caught up to range.latest. Persisted in ReaderState so
|
|
// the lost-log gate can distinguish safe drain from data loss.
|
|
message TailDrainedKey {
|
|
StreamKind stream_kind = 1;
|
|
string filer_id = 2;
|
|
int64 delay_seconds = 3; // populated when stream_kind == ORIGINAL
|
|
}
|
|
|
|
// StreamKind classifies the four blockable streams. ORIGINAL/PREDICATE pause
|
|
// reader cursors; BOOTSTRAP pauses a bucket walker; PENDING pauses a rule's
|
|
// drain task. Zero is an UNSPECIFIED sentinel — a BlockerRecord whose
|
|
// stream_kind reads as UNSPECIFIED is a corruption signal, not a default.
|
|
enum StreamKind {
|
|
STREAM_KIND_UNSPECIFIED = 0;
|
|
ORIGINAL = 1;
|
|
PREDICATE = 2;
|
|
BOOTSTRAP = 3;
|
|
PENDING = 4;
|
|
}
|
|
|
|
// BlockerRecord is the durable record of a paused stream. The cursor stays
|
|
// at the failing position until the operator clears it via
|
|
// `s3.lifecycle.blockers retry|resume|quarantine`. There is no automatic
|
|
// dead-letter — silently routing a failed delete decision aside is unsafe
|
|
// for lifecycle.
|
|
message BlockerRecord {
|
|
StreamKind stream_kind = 1;
|
|
|
|
// Stream-specific scoping; populated per stream_kind.
|
|
string shard = 2; // filer_id; ORIGINAL/PREDICATE only
|
|
int64 delay_seconds = 3; // ORIGINAL only
|
|
MessagePosition position = 4; // ORIGINAL/PREDICATE only
|
|
|
|
// Common context. bucket/object_path/version_id always populated.
|
|
// rule_hash and action_kind are OPTIONAL: empty / UNSPECIFIED for
|
|
// pre-evaluation failures (e.g. handleEvent fetchLive FATAL where no
|
|
// action has been evaluated). When populated, both are set together —
|
|
// the failure is bound to a specific (rule, action) pair.
|
|
bytes rule_hash = 5;
|
|
string bucket = 6;
|
|
string object_path = 7;
|
|
string version_id = 8;
|
|
ActionKind action_kind = 14; // optional; UNSPECIFIED for pre-evaluation failures
|
|
|
|
string reason = 9; // "FATAL_EVENT_ERROR: malformed entry"
|
|
string last_error = 10; // raw error string from the last attempt
|
|
int64 first_seen_at_ns = 11;
|
|
int64 last_retry_at_ns = 12;
|
|
int32 retry_count = 13;
|
|
}
|
|
|
|
// StreamKey is the shape used by retry-budget tracking. One of four shapes
|
|
// per stream_kind, since the identifying tuple differs.
|
|
message StreamKey {
|
|
oneof key {
|
|
OriginalKey original = 1;
|
|
PredicateKey predicate = 2;
|
|
BootstrapKey bootstrap = 3;
|
|
PendingKey pending = 4;
|
|
}
|
|
}
|
|
|
|
message OriginalKey {
|
|
string shard = 1;
|
|
int64 delay_seconds = 2;
|
|
MessagePosition position = 3;
|
|
}
|
|
message PredicateKey {
|
|
string shard = 1;
|
|
MessagePosition position = 2;
|
|
}
|
|
message BootstrapKey {
|
|
string bucket = 1;
|
|
string object_path = 2;
|
|
string version_id = 3;
|
|
bytes rule_hash = 4;
|
|
ActionKind action_kind = 5; // distinguishes per-action streams under one rule
|
|
}
|
|
message PendingKey {
|
|
string bucket = 1;
|
|
bytes rule_hash = 2;
|
|
string object_path = 3;
|
|
string version_id = 4;
|
|
ActionKind action_kind = 5;
|
|
}
|
|
|
|
// RetryBudgetEntry tracks consecutive RETRY_LATER outcomes for a stream key.
|
|
// Promotes to BLOCKED when consecutive_retries >= retryBudgetMax (30) or age
|
|
// >= retryBudgetMaxAge (4h). Compacts on success (clearRetryBudget).
|
|
message RetryBudgetEntry {
|
|
StreamKey key = 1;
|
|
int32 consecutive_retries = 2;
|
|
int64 first_seen_at_ns = 3;
|
|
int64 last_retry_at_ns = 4;
|
|
}
|
|
|
|
// RetryTarget carries routing info needed to (a) write a BlockerRecord on
|
|
// promotion and (b) re-invoke the right primitive on operator retry. We
|
|
// deliberately do NOT carry expected_identity or action: the handler
|
|
// re-fetches live state and re-evaluates from scratch.
|
|
message RetryTarget {
|
|
StreamKey key = 1;
|
|
string bucket = 2;
|
|
string object_path = 3;
|
|
string version_id = 4;
|
|
bytes rule_hash = 5;
|
|
ActionKind action_kind = 6;
|
|
}
|