futu_server/subscription/
push_regs.rs1use futu_core::qot_stock_key::QotSecurityKey;
2use futu_domain_qot_subscription::is_kl_sub_type;
3
4use super::{QotPushRegistrationLease, SubscriptionManager};
5
6impl SubscriptionManager {
7 pub fn register_push_broker(
13 &self,
14 conn_id: u64,
15 sec_key: &QotSecurityKey,
16 sub_type: i32,
17 rehab_type: i32,
18 ) {
19 self.register_push_inner(conn_id, Self::broker_key(sec_key), sub_type, rehab_type);
20 }
21
22 fn register_push_inner(
23 &self,
24 conn_id: u64,
25 key: QotSecurityKey,
26 sub_type: i32,
27 rehab_type: i32,
28 ) {
29 let effective_rehab = if is_kl_sub_type(sub_type) {
30 rehab_type
31 } else {
32 0
33 };
34 let cache_key = key.cache_key();
35 let intent_epoch = self.next_qot_push_intent_epoch();
36 let mut regs = self.qot_push_regs.write();
37 regs.by_tuple
38 .entry((key.clone(), sub_type, effective_rehab))
39 .or_default()
40 .insert(conn_id);
41 regs.qot_push_regs_by_cache_key
42 .entry(cache_key)
43 .or_default()
44 .entry((sub_type, effective_rehab))
45 .or_default()
46 .insert(conn_id, intent_epoch);
47 regs.intent_epochs.insert(
48 (key.clone(), sub_type, effective_rehab, conn_id),
49 intent_epoch,
50 );
51 drop(regs);
52 }
53
54 pub fn unregister_push_broker(
56 &self,
57 conn_id: u64,
58 sec_key: &QotSecurityKey,
59 sub_type: i32,
60 rehab_type: i32,
61 ) {
62 self.unregister_push_inner(conn_id, Self::broker_key(sec_key), sub_type, rehab_type)
63 }
64
65 fn unregister_push_inner(
66 &self,
67 conn_id: u64,
68 key: QotSecurityKey,
69 sub_type: i32,
70 rehab_type: i32,
71 ) {
72 let effective_rehab = if is_kl_sub_type(sub_type) {
73 rehab_type
74 } else {
75 0
76 };
77 let cache_key = key.cache_key();
78 let map_key = (key.clone(), sub_type, effective_rehab);
79 let mut regs = self.qot_push_regs.write();
80 regs.intent_epochs
81 .remove(&(key, sub_type, effective_rehab, conn_id));
82 if let Some(set) = regs.by_tuple.get_mut(&map_key) {
83 set.remove(&conn_id);
84 if set.is_empty() {
85 regs.by_tuple.remove(&map_key);
86 }
87 }
88 if let Some(by_sub) = regs.qot_push_regs_by_cache_key.get_mut(&cache_key) {
89 if let Some(set) = by_sub.get_mut(&(sub_type, effective_rehab)) {
90 set.remove(&conn_id);
91 if set.is_empty() {
92 by_sub.remove(&(sub_type, effective_rehab));
93 }
94 }
95 if by_sub.is_empty() {
96 regs.qot_push_regs_by_cache_key.remove(&cache_key);
97 }
98 }
99 }
100
101 pub fn get_qot_push_subscribers_broker(
104 &self,
105 sec_key: &QotSecurityKey,
106 sub_type: i32,
107 rehab_type: i32,
108 ) -> Vec<u64> {
109 self.get_qot_push_subscribers_inner(Self::broker_key(sec_key), sub_type, rehab_type)
110 }
111
112 pub fn get_qot_push_subscribers_by_cache_key(
119 &self,
120 cache_key: &str,
121 sub_type: i32,
122 rehab_type: i32,
123 ) -> Vec<u64> {
124 self.get_qot_push_subscriber_intents_by_cache_key(cache_key, sub_type, rehab_type)
125 .into_iter()
126 .map(|(conn_id, _)| conn_id)
127 .collect()
128 }
129
130 pub(crate) fn get_qot_push_subscriber_intents_by_cache_key(
135 &self,
136 cache_key: &str,
137 sub_type: i32,
138 rehab_type: i32,
139 ) -> Vec<(u64, u64)> {
140 let effective_rehab = if is_kl_sub_type(sub_type) {
141 rehab_type
142 } else {
143 0
144 };
145 let regs = self.qot_push_regs.read();
146 let mut out: Vec<(u64, u64)> = regs
147 .qot_push_regs_by_cache_key
148 .get(cache_key)
149 .and_then(|by_sub| by_sub.get(&(sub_type, effective_rehab)))
150 .map_or_else(Vec::new, |subscribers| {
151 subscribers
152 .iter()
153 .map(|(conn_id, intent_epoch)| (*conn_id, *intent_epoch))
154 .collect()
155 });
156
157 out.sort_unstable_by_key(|(conn_id, _)| *conn_id);
158 out
159 }
160
161 pub(crate) fn current_qot_push_intent_epoch_by_cache_key(
162 &self,
163 conn_id: u64,
164 cache_key: &str,
165 sub_type: i32,
166 rehab_type: i32,
167 ) -> Option<u64> {
168 let effective_rehab = if is_kl_sub_type(sub_type) {
169 rehab_type
170 } else {
171 0
172 };
173 self.qot_push_regs
174 .read()
175 .qot_push_regs_by_cache_key
176 .get(cache_key)?
177 .get(&(sub_type, effective_rehab))?
178 .get(&conn_id)
179 .copied()
180 }
181
182 pub(crate) fn with_current_qot_push_intent_by_cache_key<R>(
187 &self,
188 conn_id: u64,
189 cache_key: &str,
190 sub_type: i32,
191 rehab_type: i32,
192 expected_intent_epoch: Option<u64>,
193 publish: impl FnOnce() -> R,
194 ) -> Option<R> {
195 let effective_rehab = if is_kl_sub_type(sub_type) {
196 rehab_type
197 } else {
198 0
199 };
200 let regs = self.qot_push_regs.read();
201 let current_epoch = regs
202 .qot_push_regs_by_cache_key
203 .get(cache_key)
204 .and_then(|by_sub| by_sub.get(&(sub_type, effective_rehab)))
205 .and_then(|subscribers| subscribers.get(&conn_id))
206 .copied();
207 if current_epoch != expected_intent_epoch {
208 return None;
209 }
210 Some(publish())
211 }
212
213 fn get_qot_push_subscribers_inner(
214 &self,
215 key: QotSecurityKey,
216 sub_type: i32,
217 rehab_type: i32,
218 ) -> Vec<u64> {
219 let effective_rehab = if is_kl_sub_type(sub_type) {
220 rehab_type
221 } else {
222 0
223 };
224 match self
225 .qot_push_regs
226 .read()
227 .by_tuple
228 .get(&(key, sub_type, effective_rehab))
229 {
230 Some(subscribers) => subscribers.iter().copied().collect(),
231 None => Vec::new(),
232 }
233 }
234
235 pub fn is_push_registered_any_rehab_broker(
237 &self,
238 conn_id: u64,
239 sec_key: &QotSecurityKey,
240 sub_type: i32,
241 ) -> bool {
242 let broker_key = Self::broker_key(sec_key);
243 let pr = self.qot_push_regs.read();
244 pr.by_tuple.iter().any(|((k, st, _rehab), set)| {
245 k == &broker_key && *st == sub_type && set.contains(&conn_id)
246 })
247 }
248
249 pub fn current_qot_push_registration_lease_broker(
252 &self,
253 conn_id: u64,
254 sec_key: &QotSecurityKey,
255 sub_type: i32,
256 rehab_type: i32,
257 ) -> Option<QotPushRegistrationLease> {
258 let effective_rehab = if is_kl_sub_type(sub_type) {
259 rehab_type
260 } else {
261 0
262 };
263 let key = Self::broker_key(sec_key);
264 let regs = self.qot_push_regs.read();
265 let tuple = (key.clone(), sub_type, effective_rehab);
266 if !regs
267 .by_tuple
268 .get(&tuple)
269 .is_some_and(|connections| connections.contains(&conn_id))
270 {
271 return None;
272 }
273 let intent_epoch = regs
274 .intent_epochs
275 .get(&(key.clone(), sub_type, effective_rehab, conn_id))
276 .copied()?;
277 drop(regs);
278 Some(QotPushRegistrationLease {
279 key: key.clone(),
280 sub_type,
281 rehab_type: effective_rehab,
282 conn_id,
283 intent_epoch,
284 connection_generation: self
285 .connection_generations
286 .get(&conn_id)
287 .map(|generation| *generation)
288 .unwrap_or(0),
289 conn_session: self.get_conn_session_broker(conn_id, &key, sub_type),
290 })
291 }
292
293 pub fn with_current_qot_push_registration_lease<R>(
296 &self,
297 lease: &QotPushRegistrationLease,
298 publish: impl FnOnce() -> R,
299 ) -> Option<R> {
300 let regs = self.qot_push_regs.read();
301 let tuple = (lease.key.clone(), lease.sub_type, lease.rehab_type);
302 if !regs
303 .by_tuple
304 .get(&tuple)
305 .is_some_and(|connections| connections.contains(&lease.conn_id))
306 || regs
307 .intent_epochs
308 .get(&(
309 lease.key.clone(),
310 lease.sub_type,
311 lease.rehab_type,
312 lease.conn_id,
313 ))
314 .is_none_or(|epoch| *epoch != lease.intent_epoch)
315 {
316 return None;
317 }
318
319 let sessions = self.qot_sub_sessions.read();
320 let current_session = sessions
321 .by_key
322 .get(&(lease.key.clone(), lease.sub_type))
323 .and_then(|by_conn| by_conn.get(&lease.conn_id))
324 .copied()
325 .unwrap_or(1);
326 if current_session != lease.conn_session {
327 return None;
328 }
329
330 let current_generation = self
331 .connection_generations
332 .get(&lease.conn_id)
333 .map(|generation| *generation)
334 .unwrap_or(0);
335 if current_generation != lease.connection_generation {
336 return None;
337 }
338 Some(publish())
339 }
340}