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
c9fdc2ea
Unverified
Commit
c9fdc2ea
authored
Dec 02, 2025
by
Graham King
Committed by
GitHub
Dec 02, 2025
Browse files
fix(FileStore): Stop keep-alive thread on cancellation token. (#4666)
Signed-off-by:
Graham King
<
grahamk@nvidia.com
>
parent
71f94eda
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
27 additions
and
9 deletions
+27
-9
lib/runtime/src/distributed.rs
lib/runtime/src/distributed.rs
+1
-1
lib/runtime/src/storage/kv.rs
lib/runtime/src/storage/kv.rs
+2
-2
lib/runtime/src/storage/kv/file.rs
lib/runtime/src/storage/kv/file.rs
+24
-6
No files found.
lib/runtime/src/distributed.rs
View file @
c9fdc2ea
...
@@ -111,7 +111,7 @@ impl DistributedRuntime {
...
@@ -111,7 +111,7 @@ impl DistributedRuntime {
tracing
::
error!
(
%
err
,
"Could not connect to etcd. Pass `--store-kv ..` to use a different backend or start etcd."
))
?
;
tracing
::
error!
(
%
err
,
"Could not connect to etcd. Pass `--store-kv ..` to use a different backend or start etcd."
))
?
;
kv
::
Manager
::
etcd
(
etcd_client
)
kv
::
Manager
::
etcd
(
etcd_client
)
}
}
kv
::
Selector
::
File
(
root
)
=>
kv
::
Manager
::
file
(
root
),
kv
::
Selector
::
File
(
root
)
=>
kv
::
Manager
::
file
(
runtime
.primary_token
(),
root
),
kv
::
Selector
::
Memory
=>
kv
::
Manager
::
memory
(),
kv
::
Selector
::
Memory
=>
kv
::
Manager
::
memory
(),
};
};
...
...
lib/runtime/src/storage/kv.rs
View file @
c9fdc2ea
...
@@ -265,8 +265,8 @@ impl Manager {
...
@@ -265,8 +265,8 @@ impl Manager {
Self
::
new
(
KeyValueStoreEnum
::
Etcd
(
EtcdStore
::
new
(
etcd_client
)))
Self
::
new
(
KeyValueStoreEnum
::
Etcd
(
EtcdStore
::
new
(
etcd_client
)))
}
}
pub
fn
file
<
P
:
Into
<
PathBuf
>>
(
root
:
P
)
->
Self
{
pub
fn
file
<
P
:
Into
<
PathBuf
>>
(
cancel_token
:
CancellationToken
,
root
:
P
)
->
Self
{
Self
::
new
(
KeyValueStoreEnum
::
File
(
FileStore
::
new
(
root
)))
Self
::
new
(
KeyValueStoreEnum
::
File
(
FileStore
::
new
(
cancel_token
,
root
)))
}
}
fn
new
(
s
:
KeyValueStoreEnum
)
->
Manager
{
fn
new
(
s
:
KeyValueStoreEnum
)
->
Manager
{
...
...
lib/runtime/src/storage/kv/file.rs
View file @
c9fdc2ea
...
@@ -20,6 +20,7 @@ use async_trait::async_trait;
...
@@ -20,6 +20,7 @@ use async_trait::async_trait;
use
futures
::
StreamExt
;
use
futures
::
StreamExt
;
use
notify
::{
Config
,
Event
,
EventKind
,
RecommendedWatcher
,
RecursiveMode
,
Watcher
,
event
};
use
notify
::{
Config
,
Event
,
EventKind
,
RecommendedWatcher
,
RecursiveMode
,
Watcher
,
event
};
use
parking_lot
::
Mutex
;
use
parking_lot
::
Mutex
;
use
tokio_util
::
sync
::
CancellationToken
;
use
super
::{
Bucket
,
Key
,
KeyValue
,
Store
,
StoreError
,
StoreOutcome
,
WatchEvent
};
use
super
::{
Bucket
,
Key
,
KeyValue
,
Store
,
StoreError
,
StoreOutcome
,
WatchEvent
};
...
@@ -33,6 +34,7 @@ const MIN_KEEP_ALIVE: Duration = Duration::from_secs(1);
...
@@ -33,6 +34,7 @@ const MIN_KEEP_ALIVE: Duration = Duration::from_secs(1);
/// Treat as a singleton
/// Treat as a singleton
#[derive(Clone)]
#[derive(Clone)]
pub
struct
FileStore
{
pub
struct
FileStore
{
cancel_token
:
CancellationToken
,
root
:
PathBuf
,
root
:
PathBuf
,
connection_id
:
u64
,
connection_id
:
u64
,
/// Directories we may have created files in, for shutdown cleanup and keep-alive.
/// Directories we may have created files in, for shutdown cleanup and keep-alive.
...
@@ -41,8 +43,9 @@ pub struct FileStore {
...
@@ -41,8 +43,9 @@ pub struct FileStore {
}
}
impl
FileStore
{
impl
FileStore
{
pub
(
super
)
fn
new
<
P
:
Into
<
PathBuf
>>
(
root_dir
:
P
)
->
Self
{
pub
(
super
)
fn
new
<
P
:
Into
<
PathBuf
>>
(
cancel_token
:
CancellationToken
,
root_dir
:
P
)
->
Self
{
let
fs
=
FileStore
{
let
fs
=
FileStore
{
cancel_token
,
root
:
root_dir
.into
(),
root
:
root_dir
.into
(),
connection_id
:
rand
::
random
::
<
u64
>
(),
connection_id
:
rand
::
random
::
<
u64
>
(),
active_dirs
:
Arc
::
new
(
Mutex
::
new
(
HashMap
::
new
())),
active_dirs
:
Arc
::
new
(
Mutex
::
new
(
HashMap
::
new
())),
...
@@ -52,16 +55,27 @@ impl FileStore {
...
@@ -52,16 +55,27 @@ impl FileStore {
fs
fs
}
}
/// Keep our files alive and delete expired keys. Does not return.
/// Keep our files alive and delete expired keys.
/// We run this in a real thread so it doesn't get delayed by tokio runtime load.
///
/// It doesn't need any cleanup so we don't use cancellation token.
/// Does not return until cancellation token cancelled. On shutdown the process will
fn
expiry_thread
(
&
self
)
->
!
{
/// often exit before we detect cancellation. That's fine.
/// We run this in a real thread so it doesn't get delayed by tokio runtime under heavy load.
fn
expiry_thread
(
&
self
)
{
loop
{
loop
{
let
ttl
=
self
.shortest_ttl
();
let
ttl
=
self
.shortest_ttl
();
let
keep_alive_interval
=
cmp
::
max
(
ttl
/
3
,
MIN_KEEP_ALIVE
);
let
keep_alive_interval
=
cmp
::
max
(
ttl
/
3
,
MIN_KEEP_ALIVE
);
// Check before and after the sleep
if
self
.cancel_token
.is_cancelled
()
{
break
;
}
thread
::
sleep
(
keep_alive_interval
);
thread
::
sleep
(
keep_alive_interval
);
if
self
.cancel_token
.is_cancelled
()
{
break
;
}
self
.keep_alive
();
self
.keep_alive
();
if
let
Err
(
err
)
=
self
.delete_expired_files
()
{
if
let
Err
(
err
)
=
self
.delete_expired_files
()
{
tracing
::
error!
(
error
=
%
err
,
"FileStore delete_expired_files"
);
tracing
::
error!
(
error
=
%
err
,
"FileStore delete_expired_files"
);
...
@@ -469,13 +483,16 @@ fn to_fs_err<E: std::error::Error>(err: E) -> StoreError {
...
@@ -469,13 +483,16 @@ fn to_fs_err<E: std::error::Error>(err: E) -> StoreError {
mod
tests
{
mod
tests
{
use
std
::
collections
::
HashSet
;
use
std
::
collections
::
HashSet
;
use
tokio_util
::
sync
::
CancellationToken
;
use
crate
::
storage
::
kv
::{
Bucket
as
_
,
FileStore
,
Key
,
Store
as
_
};
use
crate
::
storage
::
kv
::{
Bucket
as
_
,
FileStore
,
Key
,
Store
as
_
};
#[tokio::test]
#[tokio::test]
async
fn
test_entries_full_path
()
{
async
fn
test_entries_full_path
()
{
let
t
=
tempfile
::
tempdir
()
.unwrap
();
let
t
=
tempfile
::
tempdir
()
.unwrap
();
let
m
=
FileStore
::
new
(
t
.path
());
let
cancel_token
=
CancellationToken
::
new
();
let
m
=
FileStore
::
new
(
cancel_token
.clone
(),
t
.path
());
let
bucket
=
m
.get_or_create_bucket
(
"v1/tests"
,
None
)
.await
.unwrap
();
let
bucket
=
m
.get_or_create_bucket
(
"v1/tests"
,
None
)
.await
.unwrap
();
let
_
=
bucket
let
_
=
bucket
.insert
(
&
Key
::
new
(
"key1/multi/part"
.to_string
()),
"value1"
.into
(),
0
)
.insert
(
&
Key
::
new
(
"key1/multi/part"
.to_string
()),
"value1"
.into
(),
0
)
...
@@ -487,6 +504,7 @@ mod tests {
...
@@ -487,6 +504,7 @@ mod tests {
.unwrap
();
.unwrap
();
let
entries
=
bucket
.entries
()
.await
.unwrap
();
let
entries
=
bucket
.entries
()
.await
.unwrap
();
let
keys
:
HashSet
<
Key
>
=
entries
.into_keys
()
.collect
();
let
keys
:
HashSet
<
Key
>
=
entries
.into_keys
()
.collect
();
cancel_token
.cancel
();
// stop the background thread
assert
!
(
keys
.contains
(
&
Key
::
new
(
"v1/tests/key1/multi/part"
.to_string
())));
assert
!
(
keys
.contains
(
&
Key
::
new
(
"v1/tests/key1/multi/part"
.to_string
())));
assert
!
(
keys
.contains
(
&
Key
::
new
(
"v1/tests/key2"
.to_string
())));
assert
!
(
keys
.contains
(
&
Key
::
new
(
"v1/tests/key2"
.to_string
())));
...
...
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