Skip to content

Commit 0d4b94b

Browse files
authored
Java: added Client API for retrieving internal statistics (#2672)
1 parent b059785 commit 0d4b94b

File tree

6 files changed

+135
-3
lines changed

6 files changed

+135
-3
lines changed

Makefile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ java:
1919
@cd java && ./gradlew :client:buildAllRelease
2020

2121
java-lint:
22-
@echo "$(GREEN)Running spotlessCheck$(RESET)"
23-
@cd java && ./gradlew :spotlessCheck
2422
@echo "$(GREEN)Running spotlessApply$(RESET)"
2523
@cd java && ./gradlew :spotlessApply
2624

java/client/src/main/java/glide/api/BaseClient.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@
275275
import glide.connectors.resources.ThreadPoolResource;
276276
import glide.connectors.resources.ThreadPoolResourceAllocator;
277277
import glide.ffi.resolvers.GlideValueResolver;
278+
import glide.ffi.resolvers.StatisticsResolver;
278279
import glide.managers.BaseResponseResolver;
279280
import glide.managers.CommandManager;
280281
import glide.managers.ConnectionManager;
@@ -385,6 +386,15 @@ protected static <T extends BaseClient> CompletableFuture<T> createClient(
385386
}
386387
}
387388

389+
/**
390+
* Return a statistics
391+
*
392+
* @return Return a {@link Map} that contains the statistics collected internally by GLIDE core
393+
*/
394+
public Map<String, String> getStatistics() {
395+
return (HashMap<String, String>) StatisticsResolver.getStatistics();
396+
}
397+
388398
/**
389399
* Return a next pubsub message if it is present.
390400
*
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/** Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 */
2+
package glide.ffi.resolvers;
3+
4+
public class StatisticsResolver {
5+
// TODO: consider lazy loading the glide_rs library
6+
static {
7+
NativeUtils.loadGlideLib();
8+
}
9+
10+
/** Return the internal statistics Map object */
11+
public static native Object getStatistics();
12+
}

java/integTest/src/test/java/glide/SharedClientTests.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ public static void init() {
4747
clusterClient =
4848
GlideClusterClient.createClient(commonClusterClientConfig().requestTimeout(10000).build())
4949
.get();
50-
5150
clients = List.of(Arguments.of(standaloneClient), Arguments.of(clusterClient));
51+
assertTrue(!clusterClient.getStatistics().isEmpty());
52+
assertEquals(
53+
clusterClient.getStatistics().size(), 2); // we expect 2 items in the statistics map
54+
55+
assertTrue(!standaloneClient.getStatistics().isEmpty());
56+
assertEquals(
57+
standaloneClient.getStatistics().size(), 2); // we expect 2 items in the statistics map
5258
}
5359

5460
@AfterAll

java/src/lib.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ use glide_core::STREAM as TYPE_STREAM;
1313
use glide_core::STRING as TYPE_STRING;
1414
use glide_core::ZSET as TYPE_ZSET;
1515

16+
// Telemetry required for getStatistics
17+
use glide_core::Telemetry;
18+
1619
use bytes::Bytes;
1720
use jni::errors::Error as JniError;
1821
use jni::objects::{JByteArray, JClass, JObject, JObjectArray, JString};
@@ -22,6 +25,7 @@ use redis::Value;
2225
use std::sync::mpsc;
2326

2427
mod errors;
28+
mod linked_hashmap;
2529

2630
use errors::{handle_errors, handle_panics, FFIError};
2731

@@ -580,6 +584,38 @@ pub extern "system" fn Java_glide_ffi_resolvers_ObjectTypeResolver_getTypeStream
580584
safe_create_jstring(env, TYPE_STREAM, "getTypeStreamConstant")
581585
}
582586

587+
/// Returns a Java's `HashMap` representing the statistics collected for this process.
588+
///
589+
/// This function is meant to be invoked by Java using JNI.
590+
///
591+
/// * `env` - The JNI environment.
592+
/// * `_class` - The class object. Not used.
593+
#[no_mangle]
594+
pub extern "system" fn Java_glide_ffi_resolvers_StatisticsResolver_getStatistics<'local>(
595+
mut env: JNIEnv<'local>,
596+
_class: JClass<'local>,
597+
) -> JObject<'local> {
598+
let Some(mut map) = linked_hashmap::new_linked_hashmap(&mut env) else {
599+
return JObject::null();
600+
};
601+
602+
linked_hashmap::put_strings(
603+
&mut env,
604+
&mut map,
605+
"total_connections",
606+
&format!("{}", Telemetry::total_connections()),
607+
);
608+
609+
linked_hashmap::put_strings(
610+
&mut env,
611+
&mut map,
612+
"total_clients",
613+
&format!("{}", Telemetry::total_clients()),
614+
);
615+
616+
map
617+
}
618+
583619
/// Convert a Rust string to a Java String and handle errors.
584620
///
585621
/// * `env` - The JNI environment.

java/src/linked_hashmap.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use crate::errors;
2+
use jni::{objects::JObject, JNIEnv};
3+
4+
const LINKED_HASHMAP: &str = "java/util/LinkedHashMap";
5+
const LINKED_HASHMAP_PUT_SIG: &str = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;";
6+
7+
/// Create new Java `LinkedHashMap`
8+
pub fn new_linked_hashmap<'a>(env: &mut JNIEnv<'a>) -> Option<JObject<'a>> {
9+
let hash_map = env.new_object(LINKED_HASHMAP, "()V", &[]);
10+
let Ok(hash_map) = hash_map else {
11+
errors::throw_java_exception(
12+
env,
13+
errors::ExceptionType::RuntimeException,
14+
"Failed to allocated LinkedHashMap",
15+
);
16+
return None;
17+
};
18+
Some(hash_map)
19+
}
20+
21+
/// Put `key` / `value` pair into the `map`, where both `key` and `value` are of type `&str`
22+
/// This method is provided for convenience
23+
pub fn put_strings<'a>(env: &mut JNIEnv<'a>, map: &mut JObject<'a>, key: &str, value: &str) {
24+
let Some(key) = string_to_jobject(env, key) else {
25+
return;
26+
};
27+
let Some(value) = string_to_jobject(env, value) else {
28+
return;
29+
};
30+
put_objects(env, map, key, value)
31+
}
32+
33+
/// Put `key` / `value` pair into the `map`, where both `key` and `value` are of type `JObject`
34+
pub fn put_objects<'a>(
35+
env: &mut JNIEnv<'a>,
36+
map: &mut JObject<'a>,
37+
key: JObject<'a>,
38+
value: JObject<'a>,
39+
) {
40+
if env
41+
.call_method(
42+
&map,
43+
"put",
44+
LINKED_HASHMAP_PUT_SIG,
45+
&[(&key).into(), (&value).into()],
46+
)
47+
.is_err()
48+
{
49+
errors::throw_java_exception(
50+
env,
51+
errors::ExceptionType::RuntimeException,
52+
"Failed to call LinkedHashMap::put method",
53+
);
54+
}
55+
}
56+
57+
/// Construct new Java string from Rust's `str`
58+
fn string_to_jobject<'a>(env: &mut JNIEnv<'a>, string: &str) -> Option<JObject<'a>> {
59+
match env.new_string(string) {
60+
Ok(obj) => Some(JObject::from(obj)),
61+
Err(_) => {
62+
errors::throw_java_exception(
63+
env,
64+
errors::ExceptionType::RuntimeException,
65+
"Failed to create Java string",
66+
);
67+
None
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)