hydracache_client_protocol/
hibernate.rs1use serde::{Deserialize, Serialize};
8
9use crate::{
10 ClientContext, ClientProtocolError, ClientRequest, Namespace, ReadConsistency, StructuredKey,
11 WriteConsistency,
12};
13
14pub const HIBERNATE_SUPPORTED_MAJOR: u8 = 6;
16
17pub const HIBERNATE_SUPPORTED_RANGE: &str = "Hibernate ORM 6.x";
19
20pub const HIBERNATE_CONTRACT_VERSION: u16 = 1;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "kebab-case")]
26pub enum L2AccessMode {
27 ReadOnly,
29 NonStrictReadWrite,
31 ReadWrite,
33 Transactional,
35}
36
37impl L2AccessMode {
38 pub const fn consistency_mapping(self) -> L2ConsistencyMapping {
40 match self {
41 Self::ReadOnly => L2ConsistencyMapping {
42 label: L2ConsistencyLabel::StrongImmutable,
43 read: ReadConsistency::Strong,
44 write: None,
45 immutable: true,
46 invalidates_on_write: false,
47 invalidates_on_commit: false,
48 joins_jvm_transaction: false,
49 },
50 Self::NonStrictReadWrite => L2ConsistencyMapping {
51 label: L2ConsistencyLabel::BestEffortInvalidate,
52 read: ReadConsistency::Eventual,
53 write: Some(WriteConsistency::Local),
54 immutable: false,
55 invalidates_on_write: true,
56 invalidates_on_commit: false,
57 joins_jvm_transaction: false,
58 },
59 Self::ReadWrite | Self::Transactional => L2ConsistencyMapping {
60 label: L2ConsistencyLabel::InvalidateOnCommit,
61 read: ReadConsistency::Session,
62 write: Some(WriteConsistency::Quorum),
63 immutable: false,
64 invalidates_on_write: false,
65 invalidates_on_commit: true,
66 joins_jvm_transaction: false,
67 },
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "kebab-case")]
75pub enum L2ConsistencyLabel {
76 StrongImmutable,
78 BestEffortInvalidate,
80 InvalidateOnCommit,
82}
83
84impl L2ConsistencyLabel {
85 pub const fn as_str(self) -> &'static str {
87 match self {
88 Self::StrongImmutable => "strong-immutable",
89 Self::BestEffortInvalidate => "best-effort-invalidate",
90 Self::InvalidateOnCommit => "invalidate-on-commit",
91 }
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
97pub struct L2ConsistencyMapping {
98 pub label: L2ConsistencyLabel,
100 pub read: ReadConsistency,
102 pub write: Option<WriteConsistency>,
104 pub immutable: bool,
106 pub invalidates_on_write: bool,
108 pub invalidates_on_commit: bool,
110 pub joins_jvm_transaction: bool,
112}
113
114impl L2ConsistencyMapping {
115 pub fn client_context(self) -> ClientContext {
117 ClientContext {
118 session_token: None,
119 read: Some(self.read),
120 write: self.write,
121 preferred_region: None,
122 }
123 }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
128#[serde(rename_all = "kebab-case")]
129pub enum HibernateRegionKind {
130 Entity,
132 Collection,
134 NaturalId,
136 Query,
138 Timestamps,
140}
141
142impl HibernateRegionKind {
143 pub const fn key_segment(self) -> &'static str {
145 match self {
146 Self::Entity => "entity",
147 Self::Collection => "collection",
148 Self::NaturalId => "natural-id",
149 Self::Query => "query",
150 Self::Timestamps => "timestamps",
151 }
152 }
153
154 pub const fn query_cache_behavior(self) -> QueryCacheBehavior {
156 match self {
157 Self::Query | Self::Timestamps => QueryCacheBehavior::TimestampBulkInvalidation,
158 Self::Entity | Self::Collection | Self::NaturalId => QueryCacheBehavior::NotQueryCache,
159 }
160 }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
165#[serde(rename_all = "kebab-case")]
166pub enum QueryCacheBehavior {
167 TimestampBulkInvalidation,
169 NotQueryCache,
171}
172
173#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
175pub struct RegionMapping {
176 pub region: String,
178 pub ns: Namespace,
180 pub kind: HibernateRegionKind,
182 pub mode: L2AccessMode,
184}
185
186impl RegionMapping {
187 pub fn new(
189 region: impl Into<String>,
190 ns: Namespace,
191 kind: HibernateRegionKind,
192 mode: L2AccessMode,
193 ) -> Result<Self, ClientProtocolError> {
194 let region = region.into();
195 if region.trim().is_empty() {
196 return Err(ClientProtocolError::InvalidField("hibernate_region"));
197 }
198 Ok(Self {
199 region,
200 ns,
201 kind,
202 mode,
203 })
204 }
205
206 pub fn from_region(
208 region: impl Into<String>,
209 kind: HibernateRegionKind,
210 mode: L2AccessMode,
211 ) -> Result<Self, ClientProtocolError> {
212 let region = region.into();
213 let ns = Namespace::new(format!("hibernate:{}", region.trim()))?;
214 Self::new(region, ns, kind, mode)
215 }
216
217 pub const fn consistency_mapping(&self) -> L2ConsistencyMapping {
219 self.mode.consistency_mapping()
220 }
221
222 pub fn client_context(&self) -> ClientContext {
224 self.consistency_mapping().client_context()
225 }
226
227 pub fn key<I, S>(&self, segments: I) -> Result<StructuredKey, ClientProtocolError>
229 where
230 I: IntoIterator<Item = S>,
231 S: Into<String>,
232 {
233 let mut key_segments = vec![self.kind.key_segment().to_owned()];
234 key_segments.extend(segments.into_iter().map(Into::into));
235 StructuredKey::new(key_segments)
236 }
237
238 pub fn get(&self, key: StructuredKey) -> ClientRequest {
240 ClientRequest::Get {
241 ns: self.ns.clone(),
242 key,
243 }
244 }
245
246 pub fn put(&self, key: StructuredKey, value: Vec<u8>, ttl_ms: Option<u64>) -> ClientRequest {
248 ClientRequest::Put {
249 ns: self.ns.clone(),
250 key,
251 value,
252 ttl_ms,
253 dimensions: vec![
254 "hibernate".to_owned(),
255 self.kind.key_segment().to_owned(),
256 self.region.clone(),
257 ],
258 }
259 }
260
261 pub fn invalidate(&self, key: StructuredKey) -> ClientRequest {
263 ClientRequest::Invalidate {
264 ns: self.ns.clone(),
265 key,
266 }
267 }
268
269 pub fn evict_region(&self) -> ClientRequest {
271 ClientRequest::EvictRegion {
272 ns: self.ns.clone(),
273 }
274 }
275}
276
277#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
279pub struct QueryCacheMapping {
280 pub query_region: RegionMapping,
282 pub timestamps_region: RegionMapping,
284}
285
286impl QueryCacheMapping {
287 pub fn new(
289 query_region: RegionMapping,
290 timestamps_region: RegionMapping,
291 ) -> Result<Self, ClientProtocolError> {
292 if query_region.kind != HibernateRegionKind::Query {
293 return Err(ClientProtocolError::InvalidField("query_region_kind"));
294 }
295 if timestamps_region.kind != HibernateRegionKind::Timestamps {
296 return Err(ClientProtocolError::InvalidField("timestamps_region_kind"));
297 }
298 Ok(Self {
299 query_region,
300 timestamps_region,
301 })
302 }
303
304 pub fn bulk_update_evictions(&self) -> [ClientRequest; 2] {
306 [
307 self.query_region.evict_region(),
308 self.timestamps_region.evict_region(),
309 ]
310 }
311}