Skip to content

Commit 8f41eda

Browse files
authored
dont 500 when creating backends with already-held keys (#627)
1 parent 3c3a305 commit 8f41eda

File tree

1 file changed

+31
-13
lines changed

1 file changed

+31
-13
lines changed

plane/src/database/connect.rs

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ use std::time::Duration;
2424

2525
const TOKEN_LIFETIME_SECONDS: u64 = 3600;
2626

27+
/// Unique violation error code in Postgres.
28+
/// NOTE: typically we should use "on conflict do nothing", but that only
29+
/// works with insert queries, not update queries.
30+
/// From: https://www.postgresql.org/docs/9.2/errcodes-appendix.html
31+
pub const PG_UNIQUE_VIOLATION_ERROR: &str = "23505";
32+
fn violates_uniqueness(err: &sqlx::Error) -> bool {
33+
if let sqlx::Error::Database(err) = &err {
34+
if let Some(code) = err.code() {
35+
return code == PG_UNIQUE_VIOLATION_ERROR;
36+
}
37+
}
38+
false
39+
}
40+
2741
type Result<T> = std::result::Result<T, ConnectError>;
2842

2943
#[derive(thiserror::Error, Debug)]
@@ -73,15 +87,16 @@ impl ConnectError {
7387
}
7488

7589
/// Attempts to create a new backend that owns the given key. If the key is already held, returns
76-
/// Ok(None). If the key is not held, creates a new backend and returns Ok(Some(backend_id)).
90+
/// Err(ConnectError::FailedToAcquireKey). If the key is not held, creates a new backend and
91+
/// returns Ok(backend_id).
7792
async fn create_backend_with_key(
7893
pool: &PgPool,
7994
key: &KeyConfig,
8095
spawn_config: &SpawnConfig,
8196
cluster: &ClusterName,
8297
drone_for_spawn: &DroneForSpawn,
8398
static_token: Option<&BearerToken>,
84-
) -> Result<Option<BackendName>> {
99+
) -> Result<BackendName> {
85100
let backend_id = spawn_config.id.clone().or_random();
86101
let mut txn = pool.begin().await?;
87102

@@ -125,11 +140,17 @@ async fn create_backend_with_key(
125140
serde_json::to_value(&BackendState::Scheduled).expect("valid json"),
126141
static_token.map(|t| t.to_string()),
127142
)
128-
.fetch_optional(&mut *txn)
129-
.await?;
130-
131-
let Some(result) = result else {
132-
return Ok(None);
143+
.fetch_one(&mut *txn)
144+
.await;
145+
146+
let result = match result {
147+
Ok(result) => result,
148+
Err(err) => {
149+
if violates_uniqueness(&err) {
150+
return Err(ConnectError::FailedToAcquireKey);
151+
}
152+
return Err(err.into());
153+
}
133154
};
134155

135156
let acquired_key = AcquiredKey {
@@ -161,7 +182,7 @@ async fn create_backend_with_key(
161182

162183
txn.commit().await?;
163184

164-
Ok(Some(backend_id))
185+
Ok(backend_id)
165186
}
166187

167188
async fn create_token(
@@ -293,18 +314,15 @@ async fn attempt_connect(
293314
.use_static_token
294315
.then(BearerToken::new_random_static);
295316

296-
let Some(backend_id) = create_backend_with_key(
317+
let backend_id = create_backend_with_key(
297318
pool,
298319
&key,
299320
spawn_config,
300321
cluster,
301322
&drone,
302323
bearer_token.as_ref(),
303324
)
304-
.await?
305-
else {
306-
return Err(ConnectError::FailedToAcquireKey);
307-
};
325+
.await?;
308326
tracing::info!(backend_id = ?backend_id, "Created backend");
309327

310328
let (token, secret_token) = if let Some(token) = bearer_token {

0 commit comments

Comments
 (0)