api: throttle streaming admin read polls
This commit is contained in:
@@ -1585,6 +1585,9 @@ impl AdminService {
|
||||
)
|
||||
})?;
|
||||
ensure_stream_session_workspace(&session, workspace_id)?;
|
||||
let session = self
|
||||
.touch_stream_session_poll_if_ready(workspace_id, session)
|
||||
.await?;
|
||||
|
||||
Ok(stream_session_detail_view(session))
|
||||
}
|
||||
@@ -1665,6 +1668,11 @@ impl AdminService {
|
||||
)
|
||||
})?;
|
||||
ensure_async_job_workspace(&job, workspace_id)?;
|
||||
let job = if matches!(job.status, JobStatus::Completed) {
|
||||
job
|
||||
} else {
|
||||
self.touch_async_job_poll_if_ready(workspace_id, job).await?
|
||||
};
|
||||
|
||||
Ok(async_job_detail_view(job))
|
||||
}
|
||||
@@ -1710,6 +1718,7 @@ impl AdminService {
|
||||
)
|
||||
})?;
|
||||
ensure_async_job_workspace(&job, workspace_id)?;
|
||||
let job = self.touch_async_job_poll_if_ready(workspace_id, job).await?;
|
||||
|
||||
match job.status {
|
||||
JobStatus::Completed => Ok(job.result.unwrap_or(Value::Null)),
|
||||
@@ -1745,6 +1754,102 @@ impl AdminService {
|
||||
}
|
||||
}
|
||||
|
||||
async fn touch_stream_session_poll_if_ready(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
session: StreamSession,
|
||||
) -> Result<StreamSession, ApiError> {
|
||||
let poll_interval_ms = self
|
||||
.streaming_poll_interval_ms(workspace_id, &session.operation_id)
|
||||
.await?;
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let remaining_delay_ms = session.remaining_poll_delay_ms(now, poll_interval_ms);
|
||||
|
||||
if remaining_delay_ms > 0 {
|
||||
return Err(ApiError::rate_limited_with_context(
|
||||
"stream session poll rate limit exceeded",
|
||||
json!({
|
||||
"session_id": session.id.as_str(),
|
||||
"poll_after_ms": remaining_delay_ms,
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
self.registry
|
||||
.touch_stream_session_poll(&session.id, &now)
|
||||
.await
|
||||
.map_err(ApiError::from)
|
||||
}
|
||||
|
||||
async fn touch_async_job_poll_if_ready(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
job: AsyncJobHandle,
|
||||
) -> Result<AsyncJobHandle, ApiError> {
|
||||
let poll_interval_ms = self
|
||||
.streaming_poll_interval_ms(workspace_id, &job.operation_id)
|
||||
.await?;
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let remaining_delay_ms = job.remaining_poll_delay_ms(now, poll_interval_ms);
|
||||
|
||||
if remaining_delay_ms > 0 {
|
||||
return Err(ApiError::rate_limited_with_context(
|
||||
"async job poll rate limit exceeded",
|
||||
json!({
|
||||
"job_id": job.id.as_str(),
|
||||
"poll_after_ms": remaining_delay_ms,
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
self.registry
|
||||
.touch_async_job_poll(&job.id, &now)
|
||||
.await
|
||||
.map_err(ApiError::from)
|
||||
}
|
||||
|
||||
async fn streaming_poll_interval_ms(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
operation_id: &OperationId,
|
||||
) -> Result<u64, ApiError> {
|
||||
let summary = self
|
||||
.registry
|
||||
.get_operation_summary(workspace_id, operation_id)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::not_found_with_context(
|
||||
format!("operation {} was not found", operation_id.as_str()),
|
||||
json!({ "operation_id": operation_id.as_str() }),
|
||||
)
|
||||
})?;
|
||||
let version = self
|
||||
.registry
|
||||
.get_operation_version(workspace_id, operation_id, summary.current_draft_version)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::not_found_with_context(
|
||||
format!(
|
||||
"operation version {} for {} was not found",
|
||||
summary.current_draft_version,
|
||||
operation_id.as_str()
|
||||
),
|
||||
json!({
|
||||
"operation_id": operation_id.as_str(),
|
||||
"version": summary.current_draft_version,
|
||||
}),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(version
|
||||
.snapshot
|
||||
.execution_config
|
||||
.streaming
|
||||
.as_ref()
|
||||
.and_then(|streaming| streaming.poll_interval_ms)
|
||||
.unwrap_or(1_000))
|
||||
}
|
||||
|
||||
pub async fn list_operations(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
@@ -2354,7 +2459,7 @@ impl AdminService {
|
||||
batch_size,
|
||||
}),
|
||||
expires_at,
|
||||
last_poll_at: Some(created_at),
|
||||
last_poll_at: None,
|
||||
created_at,
|
||||
closed_at: None,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user