pg_proto/cleanliness.rs
1//! Extensible evidence for application-owned connection reuse policy.
2
3use bytes::Bytes;
4
5use crate::codec::TransactionStatus;
6
7/// Observable facts which may affect whether an upstream connection is reusable.
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub enum CleanlinessEvent {
10 /// The backend reported its current transaction state.
11 TransactionStatus(TransactionStatus),
12 /// A reportable run-time parameter changed.
13 ParameterChanged {
14 /// Parameter name.
15 name: Bytes,
16 /// New parameter value.
17 value: Bytes,
18 },
19 /// The session began listening on a notification channel.
20 Listen {
21 /// Channel name.
22 channel: Bytes,
23 },
24 /// The session stopped listening on one or all channels.
25 Unlisten {
26 /// Channel name, or `None` for every channel.
27 channel: Option<Bytes>,
28 },
29 /// The session acquired an advisory lock.
30 AdvisoryLockAcquired,
31 /// The session released its advisory locks.
32 AdvisoryLocksReleased,
33 /// A portal became live.
34 PortalOpened {
35 /// Portal name.
36 name: Bytes,
37 },
38 /// A portal was closed.
39 PortalClosed {
40 /// Portal name.
41 name: Bytes,
42 },
43 /// A prepared statement became live.
44 StatementPrepared {
45 /// Prepared-statement name.
46 name: Bytes,
47 },
48 /// A prepared statement was closed.
49 StatementClosed {
50 /// Prepared-statement name.
51 name: Bytes,
52 },
53 /// A reset operation restored the application's clean baseline.
54 ResetComplete,
55 /// Evidence from application-specific SQL inspection.
56 Application {
57 /// Application-defined category.
58 kind: Bytes,
59 /// Application-defined supporting detail.
60 detail: Bytes,
61 },
62}
63
64/// Downstream policy which consumes cleanliness evidence.
65///
66/// The protocol library reports facts; the application decides whether they
67/// prohibit pooling and what reset operation, if any, restores reusability.
68pub trait CleanlinessPolicy {
69 /// Incorporates one observed fact into the policy's state.
70 fn observe(&mut self, event: &CleanlinessEvent);
71
72 /// Reports whether the accumulated evidence permits connection reuse.
73 fn reusable(&self) -> bool;
74}
75
76/// A no-op policy for applications which do not pool upstream connections.
77#[derive(Clone, Copy, Debug, Default)]
78pub struct IgnoreCleanliness;
79
80impl CleanlinessPolicy for IgnoreCleanliness {
81 fn observe(&mut self, _event: &CleanlinessEvent) {}
82
83 fn reusable(&self) -> bool {
84 true
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[derive(Default)]
93 struct RejectLocks(bool);
94
95 impl CleanlinessPolicy for RejectLocks {
96 fn observe(&mut self, event: &CleanlinessEvent) {
97 match event {
98 CleanlinessEvent::AdvisoryLockAcquired => self.0 = true,
99 CleanlinessEvent::AdvisoryLocksReleased | CleanlinessEvent::ResetComplete => {
100 self.0 = false;
101 }
102 _ => {}
103 }
104 }
105
106 fn reusable(&self) -> bool {
107 !self.0
108 }
109 }
110
111 #[test]
112 fn application_policy_interprets_protocol_evidence() {
113 let mut policy = RejectLocks::default();
114 policy.observe(&CleanlinessEvent::AdvisoryLockAcquired);
115 assert!(!policy.reusable());
116 policy.observe(&CleanlinessEvent::AdvisoryLocksReleased);
117 assert!(policy.reusable());
118 }
119}