Skip to content

Commit 00caa79

Browse files
tillrohrmannclaude
andcommitted
[Limiter] Add admin rule book endpoints and push to local cache
POST/PATCH/DELETE /rules write the cluster-global rule book through read_modify_write. When a worker role runs in the same process, the admin handler also pushes the new book into the local RuleBookCache via a fire-and-forget observer threaded down from the node wiring, shaving the metadata-store poll latency. Also moves UpdateField<T> out of restate-limiter into restate-serde-util since it's a generic RFC 7396 JSON Merge Patch combinator, not rule-book-specific. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e2beba6 commit 00caa79

20 files changed

Lines changed: 614 additions & 70 deletions

File tree

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/admin/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ restate-bifrost = { workspace = true, features = ["local-loglet", "replicated-lo
2828
restate-core = { workspace = true }
2929
restate-errors = { workspace = true }
3030
restate-ingestion-client = { workspace = true }
31+
restate-limiter = { workspace = true, features = ["rule-book"] }
3132
restate-metadata-store = { workspace = true }
33+
restate-util-string = { workspace = true }
3234
restate-metadata-providers = { workspace = true }
35+
restate-serde-util = { workspace = true }
3336
restate-service-client = { workspace = true }
3437
restate-service-protocol-v4 = { workspace = true, features = ["discovery", "serdes"] }
3538
restate-storage-query-datafusion = { workspace = true }
@@ -62,6 +65,7 @@ prost-dto = { workspace = true }
6265
rand = { workspace = true }
6366
serde = { workspace = true }
6467
serde_json = { workspace = true }
68+
serde_with = { workspace = true }
6569
thiserror = { workspace = true }
6670
tokio = { workspace = true }
6771
tonic = { workspace = true, features = ["transport", "codegen", "gzip", "zstd"] }

crates/admin/src/rest_api/cluster_health.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ use restate_core::{Metadata, my_node_id};
1717
use restate_types::config::Configuration;
1818
use restate_types::{NodeId, PlainNodeId};
1919

20-
use crate::rest_api::error::{ErrorDescriptionResponse, GenericRestError};
20+
use crate::rest_api::ErrorDescriptionResponse;
21+
use crate::rest_api::error::GenericRestError;
2122

2223
/// Cluster state endpoint
2324
#[utoipa::path(

crates/admin/src/rest_api/deployments.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
// the Business Source License, use of this software will be governed
99
// by the Apache License, Version 2.0.
1010

11-
use super::error::*;
12-
use crate::state::AdminServiceState;
1311
use std::time::SystemTime;
1412

1513
use axum::extract::{Path, Query, State};
1614
use axum::http::{StatusCode, header};
1715
use axum::response::IntoResponse;
1816
use axum::{Extension, Json};
1917
use http::{Method, Uri};
18+
use serde::Deserialize;
19+
2020
use restate_admin_rest_model::deployments::*;
2121
use restate_admin_rest_model::version::AdminApiVersion;
2222
use restate_errors::warn_it;
@@ -29,7 +29,10 @@ use restate_types::schema::registry::{
2929
Overwrite, TelemetryClient,
3030
};
3131
use restate_types::schema::service::ServiceMetadata;
32-
use serde::Deserialize;
32+
33+
use super::error::*;
34+
use crate::rest_api::ErrorDescriptionResponse;
35+
use crate::state::AdminServiceState;
3336

3437
/// Register deployment
3538
///

crates/admin/src/rest_api/error.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@
88
// the Business Source License, use of this software will be governed
99
// by the Apache License, Version 2.0.
1010

11+
use std::ops::RangeInclusive;
12+
1113
use axum::Json;
1214
use axum::http::StatusCode;
1315
use axum::response::{IntoResponse, Response};
1416
use codederror::{Code, CodedError};
17+
1518
use restate_core::ShutdownError;
1619
use restate_types::identifiers::{DeploymentId, SubscriptionId};
1720
use restate_types::invocation::ServiceType;
1821
use restate_types::schema::registry::SchemaRegistryError;
19-
use serde::Serialize;
20-
use std::ops::RangeInclusive;
22+
23+
use crate::rest_api::ErrorDescriptionResponse;
24+
2125
// --- Few helpers to define Admin API errors.
2226

2327
/// Macro to generate an Admin API Error enum with the given variants.
@@ -290,18 +294,6 @@ pub enum MetaApiError {
290294
DeprecatedPutDeployment,
291295
}
292296

293-
/// # Error description response
294-
///
295-
/// Error details of the response
296-
#[derive(Debug, Serialize, utoipa::ToSchema)]
297-
pub(crate) struct ErrorDescriptionResponse {
298-
message: String,
299-
/// # Restate code
300-
///
301-
/// Restate error code describing this error
302-
restate_code: Option<&'static str>,
303-
}
304-
305297
impl IntoResponse for MetaApiError {
306298
fn into_response(self) -> Response {
307299
let status_code = match &self {

crates/admin/src/rest_api/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ mod health;
1818
mod invocations;
1919
mod kafka_clusters;
2020
mod query;
21+
mod rules;
2122
mod serdes;
2223
mod services;
2324
mod subscriptions;
2425
mod version;
2526

27+
use serde::Serialize;
2628
use utoipa::OpenApi;
2729
use utoipa_axum::{router::OpenApiRouter, routes};
2830

@@ -61,6 +63,7 @@ pub use version::{MAX_ADMIN_API_VERSION, MIN_ADMIN_API_VERSION};
6163
(name = "health", description = "Admin API health"),
6264
(name = "version", description = "API Version"),
6365
(name = "introspection", description = "System introspection"),
66+
(name = "rule", description = "Limiter rule book management"),
6467
),
6568
components(responses(
6669
error::meta_api_error::BadRequest,
@@ -122,6 +125,10 @@ where
122125
.routes(routes!(kafka_clusters::get_kafka_cluster))
123126
.routes(routes!(kafka_clusters::update_kafka_cluster))
124127
.routes(routes!(kafka_clusters::delete_kafka_cluster))
128+
// Rule book endpoints
129+
.routes(routes!(rules::create_rule))
130+
.routes(routes!(rules::update_rule))
131+
.routes(routes!(rules::delete_rule))
125132
// Query endpoint
126133
.routes(routes!(query::query))
127134
};
@@ -189,3 +196,15 @@ fn create_envelope_header(partition_key: PartitionKey) -> Header {
189196
},
190197
}
191198
}
199+
200+
/// # Error description response
201+
///
202+
/// Error details of the response
203+
#[derive(Debug, Serialize, utoipa::ToSchema)]
204+
struct ErrorDescriptionResponse {
205+
message: String,
206+
/// # Restate code
207+
///
208+
/// Restate error code describing this error
209+
restate_code: Option<&'static str>,
210+
}

0 commit comments

Comments
 (0)