Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
dynamo
Commits
f30d76ce
Unverified
Commit
f30d76ce
authored
Nov 10, 2025
by
Graham King
Committed by
GitHub
Nov 10, 2025
Browse files
fix: KeyValueStore etcd impl should use internal etcd client (#4212)
Signed-off-by:
Graham King
<
grahamk@nvidia.com
>
parent
23660bc5
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
42 additions
and
51 deletions
+42
-51
lib/runtime/src/storage/key_value_store/etcd.rs
lib/runtime/src/storage/key_value_store/etcd.rs
+8
-33
lib/runtime/src/transports/etcd.rs
lib/runtime/src/transports/etcd.rs
+29
-9
lib/runtime/src/utils/leader_worker_barrier.rs
lib/runtime/src/utils/leader_worker_barrier.rs
+5
-9
No files found.
lib/runtime/src/storage/key_value_store/etcd.rs
View file @
f30d76ce
...
@@ -182,43 +182,18 @@ impl EtcdBucket {
...
@@ -182,43 +182,18 @@ impl EtcdBucket {
let
k
=
make_key
(
&
self
.bucket_name
,
key
);
let
k
=
make_key
(
&
self
.bucket_name
,
key
);
tracing
::
trace!
(
"etcd create: {k}"
);
tracing
::
trace!
(
"etcd create: {k}"
);
// Use atomic transaction to check and create in one operation
match
self
let
put_options
=
PutOptions
::
new
()
.with_lease
(
self
.client
.lease_id
()
as
i64
);
// Build transaction that creates key only if it doesn't exist
let
txn
=
Txn
::
new
()
.when
(
vec!
[
Compare
::
version
(
k
.as_str
(),
CompareOp
::
Equal
,
0
)])
// Atomic check
.and_then
(
vec!
[
TxnOp
::
put
(
k
.as_str
(),
value
,
Some
(
put_options
))])
// Only if check passes
.or_else
(
vec!
[
TxnOp
::
get
(
k
.as_str
(),
None
),
// Key exists, get its info
]);
// Execute the transaction
let
result
=
self
.client
.client
.etcd_client
()
.kv_create
(
k
.as_str
(),
value
.into
(),
None
)
.kv_client
()
.txn
(
txn
)
.await
.await
.map_err
(|
e
|
StoreError
::
EtcdError
(
e
.to_string
()))
?
;
.map_err
(|
e
|
StoreError
::
EtcdError
(
e
.to_string
()))
?
if
result
.succeeded
()
{
// Key was created successfully
return
Ok
(
StoreOutcome
::
Created
(
1
));
// version of new key is always 1
}
// Key already existed, get its version
if
let
Some
(
etcd_client
::
TxnOpResponse
::
Get
(
get_resp
))
=
result
.op_responses
()
.into_iter
()
.next
()
&&
let
Some
(
kv
)
=
get_resp
.kvs
()
.first
()
{
{
let
version
=
kv
.version
()
as
u64
;
None
=>
{
return
Ok
(
StoreOutcome
::
Exists
(
version
));
// Key was created successfully
Ok
(
StoreOutcome
::
Created
(
1
))
// version of new key is always 1
}
Some
(
revision
)
=>
Ok
(
StoreOutcome
::
Exists
(
revision
)),
}
}
// Shouldn't happen, but handle edge case
Err
(
StoreError
::
EtcdError
(
"Unexpected transaction response"
.to_string
(),
))
}
}
async
fn
update
(
async
fn
update
(
...
...
lib/runtime/src/transports/etcd.rs
View file @
f30d76ce
...
@@ -112,7 +112,7 @@ impl Client {
...
@@ -112,7 +112,7 @@ impl Client {
/// Get a clone of the underlying [`etcd_client::Client`] instance.
/// Get a clone of the underlying [`etcd_client::Client`] instance.
/// This returns a clone since the client is behind an RwLock.
/// This returns a clone since the client is behind an RwLock.
pub
fn
etcd_client
(
&
self
)
->
etcd_client
::
Client
{
fn
etcd_client
(
&
self
)
->
etcd_client
::
Client
{
self
.connector
.get_client
()
self
.connector
.get_client
()
}
}
...
@@ -121,28 +121,48 @@ impl Client {
...
@@ -121,28 +121,48 @@ impl Client {
self
.primary_lease
self
.primary_lease
}
}
pub
async
fn
kv_create
(
&
self
,
key
:
&
str
,
value
:
Vec
<
u8
>
,
lease_id
:
Option
<
u64
>
)
->
Result
<
()
>
{
/// Returns Ok(None) if value was created, Ok(Some(revision)) if the value already exists.
pub
async
fn
kv_create
(
&
self
,
key
:
&
str
,
value
:
Vec
<
u8
>
,
lease_id
:
Option
<
u64
>
,
)
->
Result
<
Option
<
u64
>>
{
let
id
=
lease_id
.unwrap_or
(
self
.lease_id
());
let
id
=
lease_id
.unwrap_or
(
self
.lease_id
());
let
put_options
=
PutOptions
::
new
()
.with_lease
(
id
as
i64
);
let
put_options
=
PutOptions
::
new
()
.with_lease
(
id
as
i64
);
// Build
the
transaction
// Build transaction
that creates key only if it doesn't exist
let
txn
=
Txn
::
new
()
let
txn
=
Txn
::
new
()
.when
(
vec!
[
Compare
::
version
(
key
,
CompareOp
::
Equal
,
0
)])
// Ensure the lock does not exist
.when
(
vec!
[
Compare
::
version
(
key
,
CompareOp
::
Equal
,
0
)])
// Ensure the lock does not exist
.and_then
(
vec!
[
.and_then
(
vec!
[
TxnOp
::
put
(
key
,
value
,
Some
(
put_options
)),
// Create the object
TxnOp
::
put
(
key
,
value
,
Some
(
put_options
)),
// Create the object
])
.or_else
(
vec!
[
TxnOp
::
get
(
key
,
None
),
// Key exists, get its info
]);
]);
// Execute the transaction
// Execute the transaction
let
result
=
self
.connector
.get_client
()
.kv_client
()
.txn
(
txn
)
.await
?
;
let
result
=
self
.connector
.get_client
()
.kv_client
()
.txn
(
txn
)
.await
?
;
// Created
if
result
.succeeded
()
{
if
result
.succeeded
()
{
Ok
(())
return
Ok
(
None
);
}
else
{
}
for
resp
in
result
.op_responses
()
{
tracing
::
warn!
(
response
=
?
resp
,
"kv_create etcd op response"
);
// Already exists
}
if
let
Some
(
etcd_client
::
TxnOpResponse
::
Get
(
get_resp
))
=
anyhow
::
bail!
(
"Unable to create key. Check etcd server status"
)
result
.op_responses
()
.into_iter
()
.next
()
&&
let
Some
(
kv
)
=
get_resp
.kvs
()
.first
()
{
let
version
=
kv
.version
()
as
u64
;
return
Ok
(
Some
(
version
));
}
// Error
for
resp
in
result
.op_responses
()
{
tracing
::
warn!
(
response
=
?
resp
,
"kv_create etcd op response"
);
}
}
anyhow
::
bail!
(
"Unable to create key. Check etcd server status"
)
}
}
/// Atomically create a key if it does not exist, or validate the values are identical if the key exists.
/// Atomically create a key if it does not exist, or validate the values are identical if the key exists.
...
...
lib/runtime/src/utils/leader_worker_barrier.rs
View file @
f30d76ce
...
@@ -90,15 +90,11 @@ async fn create_barrier_key<T: Serialize>(
...
@@ -90,15 +90,11 @@ async fn create_barrier_key<T: Serialize>(
let
serialized_data
=
let
serialized_data
=
serde_json
::
to_vec
(
&
data
)
.map_err
(
LeaderWorkerBarrierError
::
SerdeError
)
?
;
serde_json
::
to_vec
(
&
data
)
.map_err
(
LeaderWorkerBarrierError
::
SerdeError
)
?
;
// TODO: This can fail for many reasons, the most common of which is that the key already exists.
match
client
.kv_create
(
key
,
serialized_data
,
lease_id
)
.await
{
// Currently, the ETCD client returns a very generic error, so we can't distinguish between the them.
Ok
(
None
)
=>
Ok
(()),
// For now, just assume it's because the key already exists.
Ok
(
Some
(
_
))
=>
Err
(
LeaderWorkerBarrierError
::
IdNotUnique
),
client
Err
(
err
)
=>
Err
(
LeaderWorkerBarrierError
::
EtcdError
(
err
)),
.kv_create
(
key
,
serialized_data
,
lease_id
)
}
.await
.map_err
(|
_
|
LeaderWorkerBarrierError
::
IdNotUnique
)
?
;
Ok
(())
}
}
/// Waits for a single key to appear (used for completion/abort signals)
/// Waits for a single key to appear (used for completion/abort signals)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment