Skip to content

Commit a2184f5

Browse files
committed
Add NoSuchKeyError as possible errorcode
Motivation: Such checks are best performed in backend than having client checking empty or null value. This is also in-line with more critical requirement for functioning of NotFound introduced by MonitorUpdatingPersister Design. Even though there are some popular KVStore's which do not throw such exception on keyNotFound such as AWS-DDB (arguably the most popular one), we can still figure out that key didn't exist if value was returned as null or by similar means in other stores.
1 parent 5fb2d6c commit a2184f5

File tree

4 files changed

+52
-6
lines changed

4 files changed

+52
-6
lines changed

build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ fn main() {
1414
#[cfg(feature = "genproto")]
1515
fn generate_protos() {
1616
download_file(
17-
"https://raw.githubusercontent.com/lightningdevkit/vss-server/e1a88afd61f56d7e8e90a32036ca12389e36fe44/app/src/main/proto/vss.proto",
17+
"https://raw.githubusercontent.com/lightningdevkit/vss-server/62e888e1bd3305d23b15da857edffaf527163048/app/src/main/proto/vss.proto",
1818
"src/proto/vss.proto",
1919
).unwrap();
2020

src/error.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,20 @@ use std::fmt::{Display, Formatter};
1010
/// information regarding each error code and corresponding use-cases.
1111
#[derive(Debug)]
1212
pub enum VssError {
13+
/// Please refer to [`ErrorCode::NoSuchKeyException`].
14+
NoSuchKeyError(String),
15+
16+
/// Please refer to [`ErrorCode::InvalidRequestException`].
1317
InvalidRequestError(String),
18+
19+
/// Please refer to [`ErrorCode::ConflictException`].
1420
ConflictError(String),
21+
22+
/// Please refer to [`ErrorCode::InternalServerException`].
1523
InternalServerError(String),
24+
25+
/// There is an unknown error, it could be a client-side bug, unrecognized error-code, network error
26+
/// or something else.
1627
InternalError(String),
1728
}
1829

@@ -33,6 +44,9 @@ impl VssError {
3344
impl Display for VssError {
3445
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3546
match self {
47+
VssError::NoSuchKeyError(message) => {
48+
write!(f, "Requested key does not exist: {}", message)
49+
}
3650
VssError::InvalidRequestError(message) => {
3751
write!(f, "Request sent to VSS Storage was invalid: {}", message)
3852
}
@@ -54,6 +68,7 @@ impl Error for VssError {}
5468
impl From<ErrorResponse> for VssError {
5569
fn from(error_response: ErrorResponse) -> Self {
5670
match error_response.error_code() {
71+
ErrorCode::NoSuchKeyException => VssError::NoSuchKeyError(error_response.message),
5772
ErrorCode::InvalidRequestException => VssError::InvalidRequestError(error_response.message),
5873
ErrorCode::ConflictException => VssError::ConflictError(error_response.message),
5974
ErrorCode::InternalServerException => VssError::InternalServerError(error_response.message),

src/types.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ pub struct GetObjectRequest {
1010
/// Authorization and billing can also be performed at the `store_id` level.
1111
#[prost(string, tag = "1")]
1212
pub store_id: ::prost::alloc::string::String,
13-
/// `Key` for which the value is to be fetched.
13+
/// The key of the value to be fetched.
14+
///
15+
/// If the specified `key` does not exist, returns `ErrorCode.NO_SUCH_KEY_EXCEPTION` in the
16+
/// the `ErrorResponse`.
1417
///
1518
/// Consistency Guarantee:
1619
/// Get(read) operations against a `key` are consistent reads and will reflect all previous writes,
@@ -287,17 +290,19 @@ pub struct KeyValue {
287290
pub enum ErrorCode {
288291
/// Default protobuf Enum value. Will not be used as `ErrorCode` by server.
289292
Unknown = 0,
290-
/// CONFLICT_EXCEPTION is used when the request contains mismatched version (either key or global)
293+
/// Used when the request contains mismatched version (either key or global)
291294
/// in `PutObjectRequest`. For more info refer `PutObjectRequest`.
292295
ConflictException = 1,
293-
/// INVALID_REQUEST_EXCEPTION is used in the following cases:
296+
/// Used in the following cases:
294297
/// - The request was missing a required argument.
295298
/// - The specified argument was invalid, incomplete or in the wrong format.
296299
/// - The request body of api cannot be deserialized into corresponding protobuf object.
297300
InvalidRequestException = 2,
298-
/// An internal server error occurred, client is probably at no fault and can safely retry this
299-
/// error with exponential backoff.
301+
/// Used when an internal server error occurred, client is probably at no fault and can safely retry
302+
/// this error with exponential backoff.
300303
InternalServerException = 3,
304+
/// Used when the specified `key` in a `GetObjectRequest` does not exist.
305+
NoSuchKeyException = 4,
301306
}
302307
impl ErrorCode {
303308
/// String value of the enum field names used in the ProtoBuf definition.
@@ -310,6 +315,7 @@ impl ErrorCode {
310315
ErrorCode::ConflictException => "CONFLICT_EXCEPTION",
311316
ErrorCode::InvalidRequestException => "INVALID_REQUEST_EXCEPTION",
312317
ErrorCode::InternalServerException => "INTERNAL_SERVER_EXCEPTION",
318+
ErrorCode::NoSuchKeyException => "NO_SUCH_KEY_EXCEPTION",
313319
}
314320
}
315321
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -319,6 +325,7 @@ impl ErrorCode {
319325
"CONFLICT_EXCEPTION" => Some(Self::ConflictException),
320326
"INVALID_REQUEST_EXCEPTION" => Some(Self::InvalidRequestException),
321327
"INTERNAL_SERVER_EXCEPTION" => Some(Self::InternalServerException),
328+
"NO_SUCH_KEY_EXCEPTION" => Some(Self::NoSuchKeyException),
322329
_ => None,
323330
}
324331
}

tests/tests.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,30 @@ mod tests {
149149
mock_server.expect(1).assert();
150150
}
151151

152+
#[tokio::test]
153+
async fn test_no_such_key_err_handling() {
154+
let base_url = mockito::server_url();
155+
let vss_client = VssClient::new(&base_url);
156+
157+
// NoSuchKeyError
158+
let error_response = ErrorResponse {
159+
error_code: ErrorCode::NoSuchKeyException.into(),
160+
message: "NoSuchKeyException".to_string(),
161+
};
162+
let mock_server = mockito::mock("POST", GET_OBJECT_ENDPOINT)
163+
.with_status(409)
164+
.with_body(&error_response.encode_to_vec())
165+
.create();
166+
167+
let get_result = vss_client
168+
.get_object(&GetObjectRequest { store_id: "store".to_string(), key: "non_existent_key".to_string() })
169+
.await;
170+
assert!(matches!(get_result.unwrap_err(), VssError::NoSuchKeyError { .. }));
171+
172+
// Verify 1 request hit the server
173+
mock_server.expect(1).assert();
174+
}
175+
152176
#[tokio::test]
153177
async fn test_invalid_request_err_handling() {
154178
let base_url = mockito::server_url();

0 commit comments

Comments
 (0)