openstack_keystone_distributed_storage/grpc/identity_service.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14use std::sync::Arc;
15
16use tonic::Request;
17use tonic::Response;
18use tonic::Status;
19use tracing::debug;
20
21use crate::pb;
22use crate::protobuf::api::Response as PbResponse;
23use crate::protobuf::api::identity_service_server::IdentityService;
24use crate::types::StateMachineStore;
25use crate::types::*;
26
27/// External API service implementation providing key-value store operations.
28/// This service handles client requests for getting and setting values in the
29/// distributed store.
30///
31/// # Responsibilities
32/// - Ensure consistency through Raft consensus
33///
34/// # Protocol Safety
35/// This service implements the client-facing API and should validate all inputs
36/// before processing them through the Raft consensus protocol.
37pub struct IdentityServiceImpl {
38 /// The Raft node instance for consensus operations
39 raft_node: Raft,
40 /// The state machine store for direct reads
41 state_machine_store: Arc<StateMachineStore>,
42}
43
44impl IdentityServiceImpl {
45 /// Creates a new instance of the API service
46 ///
47 /// # Arguments
48 /// * `raft_node` - The Raft node instance this service will use
49 /// * `state_machine_store` - The state machine store for reading data
50 pub fn new(raft_node: Raft, state_machine_store: Arc<StateMachineStore>) -> Self {
51 Self {
52 raft_node,
53 state_machine_store,
54 }
55 }
56}
57
58#[tonic::async_trait]
59impl IdentityService for IdentityServiceImpl {
60 /// Sets a value for a given key in the distributed store
61 ///
62 /// # Arguments
63 /// * `request` - Contains the key and value to set
64 ///
65 /// # Returns
66 /// * `Ok(Response)` - Success response after the value is set
67 /// * `Err(Status)` - Error status if the set operation fails
68 #[tracing::instrument(level = "trace", skip(self))]
69 async fn set(
70 &self,
71 request: Request<pb::api::SetRequest>,
72 ) -> Result<Response<PbResponse>, Status> {
73 let req = request.into_inner();
74 debug!("Processing set request for key: {}", req.key.clone());
75
76 let res = self
77 .raft_node
78 .client_write(req.clone())
79 .await
80 .map_err(|e| Status::internal(format!("Failed to write to store: {}", e)))?;
81
82 debug!("Successfully set value for key: {}", req.key);
83 Ok(Response::new(res.data))
84 }
85
86 /// Gets a value for a given key from the distributed store
87 ///
88 /// # Arguments
89 /// * `request` - Contains the key to retrieve
90 ///
91 /// # Returns
92 /// * `Ok(Response)` - Success response containing the value
93 /// * `Err(Status)` - Error status if the get operation fails
94 #[tracing::instrument(level = "trace", skip(self))]
95 async fn get(
96 &self,
97 request: Request<pb::api::GetRequest>,
98 ) -> Result<Response<PbResponse>, Status> {
99 let req = request.into_inner();
100 debug!("Processing get request for key: {}", req.key);
101
102 let value = self
103 .state_machine_store
104 .data()
105 .get(&req.key)
106 .map_err(|_| Status::internal(format!("Key not found: {}", req.key)))?
107 .map(|x| String::from_utf8(x.to_vec()))
108 .transpose()
109 .map_err(|e| Status::internal(format!("error while converting the data, {}", e)))?;
110
111 debug!("Successfully retrieved value for key: {}", req.key);
112 Ok(Response::new(PbResponse { value }))
113 }
114}