наблюдаемость: завершить базовый контур Community
CI / Rust Checks (push) Failing after 4m28s
CI / UI Checks (push) Has been skipped
CI / Frontend E2E (push) Has been skipped
CI / Community Image Smoke (push) Has been skipped
CI / Deploy (push) Has been skipped

Добавить структурированные журналы, метрики, трассировку и безопасный канал критических ошибок. Усилить границы рантайма, тесты, проверку зависимостей и сценарии развёртывания.
This commit is contained in:
2026-07-31 01:01:14 +03:00
parent 99bd05c145
commit 0e8f1ca03a
160 changed files with 13506 additions and 1499 deletions
@@ -1,5 +1,7 @@
use super::*;
const APPLICATION_RESULT_KEY: &str = "_crank_application_result";
impl PostgresRegistry {
pub async fn create_import_job(
&self,
@@ -97,6 +99,43 @@ impl PostgresRegistry {
Ok(())
}
pub async fn apply_import_job(
&self,
request: ApplyImportJobRequest<'_>,
) -> Result<ImportJobApplyResult, RegistryError> {
let mut tx = self.pool.begin().await?;
let applied = apply_import_job_transaction(&mut tx, &request).await;
match applied {
Ok(result) => {
tx.commit().await?;
Ok(result)
}
Err(error) => {
tx.rollback().await?;
let error_text = error.to_string();
let _ = sqlx::query(
"update import_jobs
set status = $3,
error_text = $4,
finished_at = $5::timestamptz
where id = $1
and workspace_id = $2
and status <> $6",
)
.bind(request.id.as_str())
.bind(request.workspace_id.as_str())
.bind(serialize_enum_text(&ImportJobStatus::Failed, "status")?)
.bind(error_text)
.bind(request.finished_at)
.bind(serialize_enum_text(&ImportJobStatus::Completed, "status")?)
.execute(&self.pool)
.await;
Err(error)
}
}
}
pub async fn delete_expired_import_jobs(&self) -> Result<u64, RegistryError> {
let result = sqlx::query("delete from import_jobs where expires_at < now()")
.execute(&self.pool)
@@ -105,3 +144,160 @@ impl PostgresRegistry {
Ok(result.rows_affected())
}
}
async fn apply_import_job_transaction(
tx: &mut Transaction<'_, Postgres>,
request: &ApplyImportJobRequest<'_>,
) -> Result<ImportJobApplyResult, RegistryError> {
let row = sqlx::query(
"select status, preview_payload
from import_jobs
where id = $1 and workspace_id = $2
for update",
)
.bind(request.id.as_str())
.bind(request.workspace_id.as_str())
.fetch_optional(&mut **tx)
.await?
.ok_or_else(|| RegistryError::ImportJobNotFound {
job_id: request.id.as_str().to_owned(),
})?;
let status = deserialize_enum_text::<ImportJobStatus>(row.try_get("status")?, "status")?;
let mut preview_payload = row.try_get::<Value, _>("preview_payload")?;
if status == ImportJobStatus::Completed
&& let Some(result) = stored_application_result(&preview_payload)?
{
if result.application_key != request.application_key {
return Err(RegistryError::ImportJobAlreadyApplied {
job_id: request.id.as_str().to_owned(),
});
}
return Ok(result);
}
sqlx::query("select id from workspaces where id = $1 for update")
.bind(request.workspace_id.as_str())
.fetch_one(&mut **tx)
.await?;
let mut result = ImportJobApplyResult {
application_key: request.application_key.to_owned(),
..ImportJobApplyResult::default()
};
for draft in request.operations {
if draft.operation.version != 1 {
return Err(RegistryError::InvalidInitialVersion {
operation_id: draft.operation.id.as_str().to_owned(),
version: draft.operation.version,
});
}
let mut operation = draft.operation.clone();
let original_name = operation.name.clone();
if operation_name_exists(tx, request.workspace_id, &operation.name).await? {
match request.conflict_mode {
ImportConflictMode::Skip => {
result.skipped.push(SkippedImportOperation {
operation_key: draft.operation_key.clone(),
name: operation.name,
reason: "operation_name_conflict".to_owned(),
});
continue;
}
ImportConflictMode::Rename => {
operation.name =
next_available_operation_name(tx, request.workspace_id, &operation.name)
.await?;
}
}
}
insert_operation_rows(tx, request.workspace_id, &operation, None).await?;
result.created.push(AppliedImportOperation {
operation_key: draft.operation_key.clone(),
operation_id: operation.id,
name: operation.name.clone(),
version: operation.version,
renamed_from: (operation.name != original_name).then_some(original_name),
});
}
let stored_result = serde_json::to_value(&result)?;
if let Some(object) = preview_payload.as_object_mut() {
object.insert(APPLICATION_RESULT_KEY.to_owned(), stored_result);
} else {
preview_payload = serde_json::json!({
"preview": preview_payload,
"_crank_application_result": stored_result,
});
}
let created_operation_ids = serde_json::to_value(
result
.created
.iter()
.map(|operation| operation.operation_id.as_str())
.collect::<Vec<_>>(),
)?;
sqlx::query(
"update import_jobs
set status = $3,
preview_payload = $4,
created_operation_ids = $5,
error_text = null,
finished_at = $6::timestamptz
where id = $1 and workspace_id = $2",
)
.bind(request.id.as_str())
.bind(request.workspace_id.as_str())
.bind(serialize_enum_text(&ImportJobStatus::Completed, "status")?)
.bind(preview_payload)
.bind(created_operation_ids)
.bind(request.finished_at)
.execute(&mut **tx)
.await?;
Ok(result)
}
fn stored_application_result(
preview_payload: &Value,
) -> Result<Option<ImportJobApplyResult>, RegistryError> {
preview_payload
.get(APPLICATION_RESULT_KEY)
.cloned()
.map(serde_json::from_value)
.transpose()
.map_err(RegistryError::from)
}
async fn operation_name_exists(
tx: &mut Transaction<'_, Postgres>,
workspace_id: &WorkspaceId,
name: &str,
) -> Result<bool, RegistryError> {
Ok(sqlx::query_scalar::<_, bool>(
"select exists(
select 1 from operations where workspace_id = $1 and name = $2
)",
)
.bind(workspace_id.as_str())
.bind(name)
.fetch_one(&mut **tx)
.await?)
}
async fn next_available_operation_name(
tx: &mut Transaction<'_, Postgres>,
workspace_id: &WorkspaceId,
base_name: &str,
) -> Result<String, RegistryError> {
for index in 2.. {
let candidate = format!("{base_name}_{index}");
if !operation_name_exists(tx, workspace_id, &candidate).await? {
return Ok(candidate);
}
}
unreachable!()
}