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
f10e44ca
Unverified
Commit
f10e44ca
authored
Jul 31, 2025
by
Keiven C
Committed by
GitHub
Jul 31, 2025
Browse files
fix: Integration tests fixes (#2161)
Co-authored-by:
Keiven Chang
<
keivenchang@users.noreply.github.com
>
parent
7e3b3fab
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
109 additions
and
67 deletions
+109
-67
lib/runtime/src/component/component.rs
lib/runtime/src/component/component.rs
+7
-17
lib/runtime/src/component/namespace.rs
lib/runtime/src/component/namespace.rs
+7
-5
lib/runtime/src/http_server.rs
lib/runtime/src/http_server.rs
+5
-5
lib/runtime/src/metrics.rs
lib/runtime/src/metrics.rs
+12
-12
lib/runtime/tests/soak.rs
lib/runtime/tests/soak.rs
+78
-28
No files found.
lib/runtime/src/component/component.rs
View file @
f10e44ca
...
@@ -86,27 +86,17 @@ mod tests {
...
@@ -86,27 +86,17 @@ mod tests {
// todo - make a distributed runtime fixture
// todo - make a distributed runtime fixture
// todo - two options - fully mocked or integration test
// todo - two options - fully mocked or integration test
#[tokio::test]
#[tokio::test]
async
fn
test_publish
()
{
async
fn
test_publish
_and_subscribe
()
{
let
rt
=
Runtime
::
from_current
()
.unwrap
();
let
rt
=
Runtime
::
from_current
()
.unwrap
();
let
dtr
=
DistributedRuntime
::
from_settings
(
rt
.clone
())
.await
.unwrap
();
let
dtr
=
DistributedRuntime
::
from_settings
(
rt
.clone
())
.await
.unwrap
();
let
ns
=
dtr
.namespace
(
"test"
.to_string
())
.unwrap
();
let
ns
=
dtr
.namespace
(
"test_component"
.to_string
())
.unwrap
();
let
cp
=
ns
.component
(
"component"
.to_string
())
.unwrap
();
let
cp
=
ns
.component
(
"test_component"
.to_string
())
.unwrap
();
cp
.publish
(
"test"
,
&
"test"
.to_string
())
.await
.unwrap
();
rt
.shutdown
();
}
#[tokio::test]
async
fn
test_subscribe
()
{
let
rt
=
Runtime
::
from_current
()
.unwrap
();
let
dtr
=
DistributedRuntime
::
from_settings
(
rt
.clone
())
.await
.unwrap
();
let
ns
=
dtr
.namespace
(
"test"
.to_string
())
.unwrap
();
let
cp
=
ns
.component
(
"component"
.to_string
())
.unwrap
();
// Create a subscriber
// Create a subscriber
on the component
let
mut
subscriber
=
ns
.subscribe
(
"test"
)
.await
.unwrap
();
let
mut
subscriber
=
cp
.subscribe
(
"test
_event
"
)
.await
.unwrap
();
// Publish a message
// Publish a message
from the component
cp
.publish
(
"test"
,
&
"test_message"
.to_string
())
cp
.publish
(
"test
_event
"
,
&
"test_message"
.to_string
())
.await
.await
.unwrap
();
.unwrap
();
...
...
lib/runtime/src/component/namespace.rs
View file @
f10e44ca
...
@@ -99,8 +99,8 @@ mod tests {
...
@@ -99,8 +99,8 @@ mod tests {
async
fn
test_publish
()
{
async
fn
test_publish
()
{
let
rt
=
Runtime
::
from_current
()
.unwrap
();
let
rt
=
Runtime
::
from_current
()
.unwrap
();
let
dtr
=
DistributedRuntime
::
from_settings
(
rt
.clone
())
.await
.unwrap
();
let
dtr
=
DistributedRuntime
::
from_settings
(
rt
.clone
())
.await
.unwrap
();
let
ns
=
dtr
.namespace
(
"test"
.to_string
())
.unwrap
();
let
ns
=
dtr
.namespace
(
"test
_namespace_publish
"
.to_string
())
.unwrap
();
ns
.publish
(
"test"
,
&
"test"
.to_string
())
.await
.unwrap
();
ns
.publish
(
"test
_event
"
,
&
"test"
.to_string
())
.await
.unwrap
();
rt
.shutdown
();
rt
.shutdown
();
}
}
...
@@ -108,13 +108,15 @@ mod tests {
...
@@ -108,13 +108,15 @@ mod tests {
async
fn
test_subscribe
()
{
async
fn
test_subscribe
()
{
let
rt
=
Runtime
::
from_current
()
.unwrap
();
let
rt
=
Runtime
::
from_current
()
.unwrap
();
let
dtr
=
DistributedRuntime
::
from_settings
(
rt
.clone
())
.await
.unwrap
();
let
dtr
=
DistributedRuntime
::
from_settings
(
rt
.clone
())
.await
.unwrap
();
let
ns
=
dtr
.namespace
(
"test"
.to_string
())
.unwrap
();
let
ns
=
dtr
.namespace
(
"test_namespace_subscribe"
.to_string
())
.unwrap
();
// Create a subscriber
// Create a subscriber
let
mut
subscriber
=
ns
.subscribe
(
"test"
)
.await
.unwrap
();
let
mut
subscriber
=
ns
.subscribe
(
"test
_event
"
)
.await
.unwrap
();
// Publish a message
// Publish a message
ns
.publish
(
"test"
,
&
"test_message"
.to_string
())
ns
.publish
(
"test
_event
"
,
&
"test_message"
.to_string
())
.await
.await
.unwrap
();
.unwrap
();
...
...
lib/runtime/src/http_server.rs
View file @
f10e44ca
...
@@ -77,7 +77,7 @@ impl crate::traits::DistributedRuntimeProvider for HttpMetricsRegistry {
...
@@ -77,7 +77,7 @@ impl crate::traits::DistributedRuntimeProvider for HttpMetricsRegistry {
impl
MetricsRegistry
for
HttpMetricsRegistry
{
impl
MetricsRegistry
for
HttpMetricsRegistry
{
fn
basename
(
&
self
)
->
String
{
fn
basename
(
&
self
)
->
String
{
"
http_server
"
.to_string
()
"
dynamo
"
.to_string
()
}
}
fn
parent_hierarchy
(
&
self
)
->
Vec
<
String
>
{
fn
parent_hierarchy
(
&
self
)
->
Vec
<
String
>
{
...
@@ -100,7 +100,7 @@ impl HttpServerState {
...
@@ -100,7 +100,7 @@ impl HttpServerState {
// Note: This metric is created at the DRT level (no namespace), so we manually add "dynamo_" prefix
// Note: This metric is created at the DRT level (no namespace), so we manually add "dynamo_" prefix
// to maintain consistency with the project's metric naming convention
// to maintain consistency with the project's metric naming convention
let
uptime_gauge
=
http_metrics_registry
.as_ref
()
.create_gauge
(
let
uptime_gauge
=
http_metrics_registry
.as_ref
()
.create_gauge
(
"
dynamo
_uptime_seconds"
,
"
system
_uptime_seconds"
,
"Total uptime of the DistributedRuntime in seconds"
,
"Total uptime of the DistributedRuntime in seconds"
,
&
[],
&
[],
)
?
;
)
?
;
...
@@ -368,9 +368,9 @@ mod tests {
...
@@ -368,9 +368,9 @@ mod tests {
println!
(
"Full metrics response:
\n
{}"
,
response
);
println!
(
"Full metrics response:
\n
{}"
,
response
);
let
expected
=
"
\
let
expected
=
"
\
# HELP dynamo_uptime_seconds Total uptime of the DistributedRuntime in seconds
# HELP dynamo_
system_
uptime_seconds Total uptime of the DistributedRuntime in seconds
# TYPE dynamo_uptime_seconds gauge
# TYPE dynamo_
system_
uptime_seconds gauge
dynamo_uptime_seconds{namespace=
\"
http_server
\"
} 42
dynamo_
system_
uptime_seconds{namespace=
\"
dynamo
\"
} 42
"
;
"
;
assert_eq!
(
response
,
expected
);
assert_eq!
(
response
,
expected
);
}
}
...
...
lib/runtime/src/metrics.rs
View file @
f10e44ca
...
@@ -797,7 +797,7 @@ mod test_prefixes {
...
@@ -797,7 +797,7 @@ mod test_prefixes {
println!
(
"
\n
=== Testing Invalid Namespace Behavior ==="
);
println!
(
"
\n
=== Testing Invalid Namespace Behavior ==="
);
// Create a namespace with invalid name (contains hyphen)
// Create a namespace with invalid name (contains hyphen)
let
invalid_namespace
=
drt
.namespace
(
"
test-namespace
"
)
.unwrap
();
let
invalid_namespace
=
drt
.namespace
(
"
@@123
"
)
.unwrap
();
// Debug: Let's see what the hierarchy looks like
// Debug: Let's see what the hierarchy looks like
println!
(
println!
(
...
@@ -810,15 +810,15 @@ mod test_prefixes {
...
@@ -810,15 +810,15 @@ mod test_prefixes {
);
);
println!
(
"Invalid namespace prefix: '{}'"
,
invalid_namespace
.prefix
());
println!
(
"Invalid namespace prefix: '{}'"
,
invalid_namespace
.prefix
());
// Try to create a metric - this should fail because
the namespace name will be used in the metric name
// Try to create a metric - this should fail because
"@@123" gets stripped to "" which is invalid
let
result
=
invalid_namespace
.create_counter
(
"test_counter"
,
"A test counter"
,
&
[]);
let
result
=
invalid_namespace
.create_counter
(
"test_counter"
,
"A test counter"
,
&
[]);
println!
(
"Result with invalid namespace '
test-namespace
':"
);
println!
(
"Result with invalid namespace '
@@123
':"
);
println!
(
"{:?}"
,
result
);
println!
(
"{:?}"
,
result
);
// The result should be an error
from Prometheus
// The result should be an error
because empty metric names are invalid
assert
!
(
assert
!
(
result
.is_err
(),
result
.is_err
(),
"Creating metric with
invalid
namespace should fail"
"Creating metric with namespace
'@@123'
should fail
because it gets stripped to empty string
"
);
);
// For comparison, show a valid namespace works
// For comparison, show a valid namespace works
...
@@ -926,15 +926,15 @@ testnamespace_testgauge{{component="testcomponent",namespace="testnamespace"}} 5
...
@@ -926,15 +926,15 @@ testnamespace_testgauge{{component="testcomponent",namespace="testnamespace"}} 5
println!
(
"{}"
,
namespace_output
);
println!
(
"{}"
,
namespace_output
);
let
expected_namespace_output
=
format!
(
let
expected_namespace_output
=
format!
(
r#"# HELP testintcounter A test int counter
r#"# HELP testnamespace_testcounter A test counter
# TYPE testintcounter counter
testintcounter{{namespace="testnamespace"}} 12345
# HELP testnamespace_testcounter A test counter
# TYPE testnamespace_testcounter counter
# TYPE testnamespace_testcounter counter
testnamespace_testcounter{{component="testcomponent",endpoint="testendpoint",namespace="testnamespace"}} 123.456789
testnamespace_testcounter{{component="testcomponent",endpoint="testendpoint",namespace="testnamespace"}} 123.456789
# HELP testnamespace_testgauge A test gauge
# HELP testnamespace_testgauge A test gauge
# TYPE testnamespace_testgauge gauge
# TYPE testnamespace_testgauge gauge
testnamespace_testgauge{{component="testcomponent",namespace="testnamespace"}} 50000
testnamespace_testgauge{{component="testcomponent",namespace="testnamespace"}} 50000
# HELP testnamespace_testintcounter A test int counter
# TYPE testnamespace_testintcounter counter
testnamespace_testintcounter{{namespace="testnamespace"}} 12345
"#
"#
);
);
...
@@ -1015,9 +1015,6 @@ testhistogram_bucket{{le="10"}} 3
...
@@ -1015,9 +1015,6 @@ testhistogram_bucket{{le="10"}} 3
testhistogram_bucket{{le="+Inf"}} 3
testhistogram_bucket{{le="+Inf"}} 3
testhistogram_sum 7.5
testhistogram_sum 7.5
testhistogram_count 3
testhistogram_count 3
# HELP testintcounter A test int counter
# TYPE testintcounter counter
testintcounter{{namespace="testnamespace"}} 12345
# HELP testintgauge A test int gauge
# HELP testintgauge A test int gauge
# TYPE testintgauge gauge
# TYPE testintgauge gauge
testintgauge 42
testintgauge 42
...
@@ -1031,6 +1028,9 @@ testnamespace_testcounter{{component="testcomponent",endpoint="testendpoint",nam
...
@@ -1031,6 +1028,9 @@ testnamespace_testcounter{{component="testcomponent",endpoint="testendpoint",nam
# HELP testnamespace_testgauge A test gauge
# HELP testnamespace_testgauge A test gauge
# TYPE testnamespace_testgauge gauge
# TYPE testnamespace_testgauge gauge
testnamespace_testgauge{{component="testcomponent",namespace="testnamespace"}} 50000
testnamespace_testgauge{{component="testcomponent",namespace="testnamespace"}} 50000
# HELP testnamespace_testintcounter A test int counter
# TYPE testnamespace_testintcounter counter
testnamespace_testintcounter{{namespace="testnamespace"}} 12345
"#
"#
);
);
...
...
lib/runtime/tests/soak.rs
View file @
f10e44ca
...
@@ -13,6 +13,17 @@
...
@@ -13,6 +13,17 @@
// See the License for the specific language governing permissions and
// See the License for the specific language governing permissions and
// limitations under the License.
// limitations under the License.
// cargo test --test soak integration::main --features integration
//!
//! It will send a batch of requests to the runtime and measure the throughput.
//!
//! It will also measure the latency of the requests.
//!
//! A reasonable soak test configuration to start off is 1 minute duration with 10000 batch load:
//! export DYN_QUEUED_UP_PROCESSING=true
//! export DYN_SOAK_BATCH_LOAD=10000
//! export DYN_SOAK_RUN_DURATION=60s
//! cargo test --test soak integration::main --features integration -- --nocapture
#[cfg(feature
=
"integration"
)]
#[cfg(feature
=
"integration"
)]
mod
integration
{
mod
integration
{
...
@@ -22,13 +33,17 @@ mod integration {
...
@@ -22,13 +33,17 @@ mod integration {
logging
,
logging
,
pipeline
::{
pipeline
::{
async_trait
,
network
::
Ingress
,
AsyncEngine
,
AsyncEngineContextProvider
,
Error
,
ManyOut
,
async_trait
,
network
::
Ingress
,
AsyncEngine
,
AsyncEngineContextProvider
,
Error
,
ManyOut
,
ResponseStream
,
SingleIn
,
PushRouter
,
ResponseStream
,
SingleIn
,
},
},
protocols
::
annotated
::
Annotated
,
protocols
::
annotated
::
Annotated
,
DistributedRuntime
,
ErrorContext
,
Result
,
Runtime
,
Worker
,
stream
,
DistributedRuntime
,
ErrorContext
,
Result
,
Runtime
,
Worker
,
};
};
use
futures
::
StreamExt
;
use
futures
::
StreamExt
;
use
std
::{
sync
::
Arc
,
time
::
Duration
};
use
std
::{
sync
::
atomic
::{
AtomicU64
,
Ordering
},
sync
::
Arc
,
time
::
Duration
,
};
use
tokio
::
time
::
Instant
;
use
tokio
::
time
::
Instant
;
#[test]
#[test]
...
@@ -45,16 +60,29 @@ mod integration {
...
@@ -45,16 +60,29 @@ mod integration {
client
.await
??
;
client
.await
??
;
distributed
.shutdown
();
distributed
.shutdown
();
server
.await
??
;
let
handler
=
server
.await
??
;
// Print final backend counter value
let
final_count
=
handler
.backend_counter
.load
(
Ordering
::
Relaxed
);
println!
(
"Final RequestHandler backend_counter: {} requests processed"
,
final_count
);
Ok
(())
Ok
(())
}
}
struct
RequestHandler
{}
struct
RequestHandler
{
backend_counter
:
AtomicU64
,
queued_up_processing
:
bool
,
}
impl
RequestHandler
{
impl
RequestHandler
{
fn
new
()
->
Arc
<
Self
>
{
fn
new
(
queued_up_processing
:
bool
)
->
Arc
<
Self
>
{
Arc
::
new
(
Self
{})
Arc
::
new
(
Self
{
backend_counter
:
AtomicU64
::
new
(
0
),
queued_up_processing
,
})
}
}
}
}
...
@@ -63,25 +91,40 @@ mod integration {
...
@@ -63,25 +91,40 @@ mod integration {
async
fn
generate
(
&
self
,
input
:
SingleIn
<
String
>
)
->
Result
<
ManyOut
<
Annotated
<
String
>>>
{
async
fn
generate
(
&
self
,
input
:
SingleIn
<
String
>
)
->
Result
<
ManyOut
<
Annotated
<
String
>>>
{
let
(
data
,
ctx
)
=
input
.into_parts
();
let
(
data
,
ctx
)
=
input
.into_parts
();
// Increment backend counter
self
.backend_counter
.fetch_add
(
1
,
Ordering
::
Relaxed
);
let
chars
=
data
let
chars
=
data
.chars
()
.chars
()
.map
(|
c
|
Annotated
::
from_data
(
c
.to_string
()))
.map
(|
c
|
Annotated
::
from_data
(
c
.to_string
()))
.collect
::
<
Vec
<
_
>>
();
.collect
::
<
Vec
<
_
>>
();
let
stream
=
async_stream
::
stream!
{
if
self
.queued_up_processing
{
for
c
in
chars
{
// queued up processing - delayed response to saturate the queue
yield
c
;
let
async_stream
=
async_stream
::
stream!
{
tokio
::
time
::
sleep
(
tokio
::
time
::
Duration
::
from_millis
(
100
))
.await
;
for
c
in
chars
{
}
yield
c
;
};
tokio
::
time
::
sleep
(
tokio
::
time
::
Duration
::
from_millis
(
100
))
.await
;
}
Ok
(
ResponseStream
::
new
(
Box
::
pin
(
stream
),
ctx
.context
()))
};
Ok
(
ResponseStream
::
new
(
Box
::
pin
(
async_stream
),
ctx
.context
()))
}
else
{
// normal processing - immediate response
let
iter_stream
=
stream
::
iter
(
chars
);
Ok
(
ResponseStream
::
new
(
Box
::
pin
(
iter_stream
),
ctx
.context
()))
}
}
}
}
}
async
fn
backend
(
runtime
:
DistributedRuntime
)
->
Result
<
()
>
{
async
fn
backend
(
runtime
:
DistributedRuntime
)
->
Result
<
Arc
<
RequestHandler
>>
{
// get the queued up processing setting from env (not delayed)
let
queued_up_processing
=
std
::
env
::
var
(
"DYN_QUEUED_UP_PROCESSING"
)
.unwrap_or
(
"false"
.to_string
());
let
queued_up_processing
:
bool
=
queued_up_processing
.parse
()
.unwrap_or
(
false
);
// attach an ingress to an engine
// attach an ingress to an engine
let
ingress
=
Ingress
::
for_engine
(
RequestHandler
::
new
())
?
;
let
handler
=
RequestHandler
::
new
(
queued_up_processing
);
let
ingress
=
Ingress
::
for_engine
(
handler
.clone
())
?
;
// // make the ingress discoverable via a component service
// // make the ingress discoverable via a component service
// // we must first create a service, then we can attach one more more endpoints
// // we must first create a service, then we can attach one more more endpoints
...
@@ -95,27 +138,32 @@ mod integration {
...
@@ -95,27 +138,32 @@ mod integration {
.endpoint_builder
()
.endpoint_builder
()
.handler
(
ingress
)
.handler
(
ingress
)
.start
()
.start
()
.await
.await
?
;
Ok
(
handler
)
}
}
async
fn
client
(
runtime
:
DistributedRuntime
)
->
Result
<
()
>
{
async
fn
client
(
runtime
:
DistributedRuntime
)
->
Result
<
()
>
{
// get the run duration from env
// get the run duration from env
let
run_duration
=
std
::
env
::
var
(
"DYN_SOAK_RUN_DURATION"
)
.unwrap_or
(
"
1m
"
.to_string
());
let
run_duration
=
std
::
env
::
var
(
"DYN_SOAK_RUN_DURATION"
)
.unwrap_or
(
"
3s
"
.to_string
());
let
run_duration
=
let
run_duration
=
humantime
::
parse_duration
(
&
run_duration
)
.unwrap_or
(
Duration
::
from_secs
(
60
));
humantime
::
parse_duration
(
&
run_duration
)
.unwrap_or
(
Duration
::
from_secs
(
3
));
let
batch_load
=
std
::
env
::
var
(
"DYN_SOAK_BATCH_LOAD"
)
.unwrap_or
(
"100
00
"
.to_string
());
let
batch_load
=
std
::
env
::
var
(
"DYN_SOAK_BATCH_LOAD"
)
.unwrap_or
(
"100"
.to_string
());
let
batch_load
:
usize
=
batch_load
.parse
()
.unwrap_or
(
100
00
);
let
batch_load
:
usize
=
batch_load
.parse
()
.unwrap_or
(
100
);
let
client
=
runtime
let
client
=
runtime
.namespace
(
DEFAULT_NAMESPACE
)
?
.namespace
(
DEFAULT_NAMESPACE
)
?
.component
(
"backend"
)
?
.component
(
"backend"
)
?
.endpoint
(
"generate"
)
.endpoint
(
"generate"
)
.client
::
<
String
,
Annotated
<
String
>>
()
.client
()
.await
?
;
.await
?
;
client
.wait_for_instances
()
.await
?
;
client
.wait_for_instances
()
.await
?
;
let
client
=
Arc
::
new
(
client
);
let
router
=
PushRouter
::
<
String
,
Annotated
<
String
>>
::
from_client
(
client
,
Default
::
default
())
.await
?
;
let
router
=
Arc
::
new
(
router
);
let
start
=
Instant
::
now
();
let
start
=
Instant
::
now
();
let
mut
count
=
0
;
let
mut
count
=
0
;
...
@@ -123,11 +171,11 @@ mod integration {
...
@@ -123,11 +171,11 @@ mod integration {
loop
{
loop
{
let
mut
tasks
=
Vec
::
new
();
let
mut
tasks
=
Vec
::
new
();
for
_
in
0
..
batch_load
{
for
_
in
0
..
batch_load
{
let
client
=
client
.clone
();
let
router
=
router
.clone
();
tasks
.push
(
tokio
::
spawn
(
async
move
{
tasks
.push
(
tokio
::
spawn
(
async
move
{
let
mut
stream
=
tokio
::
time
::
timeout
(
let
mut
stream
=
tokio
::
time
::
timeout
(
Duration
::
from_secs
(
30
),
Duration
::
from_secs
(
5
),
client
.random
(
"hello world"
.to_string
()
.into
()),
router
.random
(
"hello world"
.to_string
()
.into
()),
)
)
.await
.await
.context
(
"request timed out"
)
??
;
.context
(
"request timed out"
)
??
;
...
@@ -147,7 +195,9 @@ mod integration {
...
@@ -147,7 +195,9 @@ mod integration {
let
elapsed
=
start
.elapsed
();
let
elapsed
=
start
.elapsed
();
count
+=
batch_load
;
count
+=
batch_load
;
println!
(
"elapsed: {:?}; count: {}"
,
elapsed
,
count
);
if
count
%
1000
==
0
{
println!
(
"elapsed: {:?}; count: {}"
,
elapsed
,
count
);
}
if
elapsed
>
run_duration
{
if
elapsed
>
run_duration
{
println!
(
"done"
);
println!
(
"done"
);
...
...
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