1
use std::{error::Error as StdError, num::NonZeroUsize, sync::Arc};
2

            
3
use async_trait::async_trait;
4
use lru::LruCache;
5
use tokio::sync::RwLock;
6

            
7
use super::super::{
8
    device::{QueryCond as DeviceQueryCond, QueryOneCond as DeviceQueryOneCond},
9
    device_route::{
10
        DelCachePubQueryCond, DelCacheQueryCond, DeviceRouteCache, DeviceRouteCacheDlData,
11
        DeviceRouteCacheUlData, GetCachePubQueryCond, GetCacheQueryCond, ListOptions,
12
        ListQueryCond,
13
    },
14
    Model,
15
};
16

            
17
pub struct Cache {
18
    model: Arc<dyn Model>,
19
    uldata: Arc<RwLock<LruCache<String, Option<DeviceRouteCacheUlData>>>>,
20
    dldata: Arc<RwLock<LruCache<String, Option<DeviceRouteCacheDlData>>>>,
21
    dldata_pub: Arc<RwLock<LruCache<String, Option<DeviceRouteCacheDlData>>>>,
22
}
23

            
24
pub struct Options {
25
    pub uldata_size: usize,
26
    pub dldata_size: usize,
27
    pub dldata_pub_size: usize,
28
}
29

            
30
const DEF_SIZE: usize = 10_000;
31

            
32
impl Cache {
33
10
    pub fn new(opts: &Options, model: Arc<dyn Model>) -> Self {
34
10
        let (uldata, dldata, dldata_pub) = unsafe {
35
10
            (
36
10
                NonZeroUsize::new_unchecked(opts.uldata_size),
37
10
                NonZeroUsize::new_unchecked(opts.dldata_size),
38
10
                NonZeroUsize::new_unchecked(opts.dldata_pub_size),
39
10
            )
40
10
        };
41
10
        Cache {
42
10
            model,
43
10
            uldata: Arc::new(RwLock::new(LruCache::new(uldata))),
44
10
            dldata: Arc::new(RwLock::new(LruCache::new(dldata))),
45
10
            dldata_pub: Arc::new(RwLock::new(LruCache::new(dldata_pub))),
46
10
        }
47
10
    }
48
}
49

            
50
#[async_trait]
51
impl DeviceRouteCache for Cache {
52
50
    async fn clear(&self) -> Result<(), Box<dyn StdError>> {
53
        // To collect all locks before clearing cache.
54
50
        let mut lock1 = self.uldata.write().await;
55
50
        let mut lock2 = self.dldata.write().await;
56
50
        let mut lock3 = self.dldata_pub.write().await;
57
50
        lock1.clear();
58
50
        lock2.clear();
59
50
        lock3.clear();
60
50
        Ok(())
61
100
    }
62

            
63
    async fn get_uldata(
64
        &self,
65
        device_id: &str,
66
26
    ) -> Result<Option<DeviceRouteCacheUlData>, Box<dyn StdError>> {
67
        {
68
26
            let mut lock = self.uldata.write().await;
69
26
            if let Some(value) = lock.get(device_id) {
70
6
                return Ok(value.clone());
71
20
            }
72
20
        }
73
20

            
74
20
        let opts = ListOptions {
75
20
            cond: &ListQueryCond {
76
20
                device_id: Some(device_id),
77
20
                ..Default::default()
78
20
            },
79
20
            offset: None,
80
20
            limit: None,
81
20
            sort: None,
82
20
            cursor_max: None,
83
20
        };
84
20
        let (mut routes, _) = self.model.device_route().list(&opts, None).await?;
85
20
        let data: Option<DeviceRouteCacheUlData> = match routes.len() {
86
6
            0 => None,
87
            _ => {
88
14
                let mut routes_data = vec![];
89
16
                for r in routes.iter() {
90
16
                    routes_data.push(format!("{}.{}", r.unit_code, r.application_code))
91
                }
92
14
                let _ = routes.pop().unwrap();
93
14
                Some(DeviceRouteCacheUlData {
94
14
                    app_mgr_keys: routes_data,
95
14
                })
96
            }
97
        };
98
20
        let _ = self.set_uldata(device_id, data.as_ref()).await;
99
20
        Ok(data)
100
52
    }
101

            
102
    async fn set_uldata(
103
        &self,
104
        device_id: &str,
105
        value: Option<&DeviceRouteCacheUlData>,
106
20
    ) -> Result<(), Box<dyn StdError>> {
107
20
        let key = device_id.to_string();
108
20
        let mut lock = self.uldata.write().await;
109
20
        let _ = match value {
110
6
            None => lock.push(key, None),
111
14
            Some(value) => lock.push(key, Some(value.clone())),
112
        };
113
20
        Ok(())
114
40
    }
115

            
116
34909
    async fn del_uldata(&self, device_id: &str) -> Result<(), Box<dyn StdError>> {
117
34909
        let mut lock = self.uldata.write().await;
118
34909
        lock.pop(device_id);
119
34909
        Ok(())
120
69818
    }
121

            
122
    async fn get_dldata(
123
        &self,
124
        cond: &GetCacheQueryCond,
125
10
    ) -> Result<Option<DeviceRouteCacheDlData>, Box<dyn StdError>> {
126
10
        let key = format!(
127
10
            "{}.{}.{}",
128
10
            cond.unit_code, cond.network_code, cond.network_addr
129
10
        );
130

            
131
        {
132
10
            let mut lock = self.dldata.write().await;
133
10
            if let Some(value) = lock.get(&key) {
134
2
                match value {
135
1
                    None => return Ok(None),
136
1
                    Some(value) => return Ok(Some(value.clone())),
137
                }
138
8
            }
139
8
        }
140
8

            
141
8
        let dev_cond = DeviceQueryCond {
142
8
            device: Some(DeviceQueryOneCond {
143
8
                unit_code: Some(cond.unit_code),
144
8
                network_code: cond.network_code,
145
8
                network_addr: cond.network_addr,
146
8
            }),
147
8
            ..Default::default()
148
8
        };
149
8
        let device = self.model.device().get(&dev_cond).await?;
150
8
        let data = match device {
151
2
            None => None,
152
6
            Some(device) => match device.unit_code.as_ref() {
153
                // This should not occur!
154
                None => None,
155
6
                Some(unit_code) => Some(DeviceRouteCacheDlData {
156
6
                    net_mgr_key: format!("{}.{}", unit_code, cond.network_code),
157
6
                    network_id: device.network_id,
158
6
                    network_addr: device.network_addr,
159
6
                    device_id: device.device_id,
160
6
                    profile: device.profile,
161
6
                }),
162
            },
163
        };
164
8
        let _ = self.set_dldata(cond, data.as_ref()).await;
165
8
        Ok(data)
166
20
    }
167

            
168
    async fn set_dldata(
169
        &self,
170
        cond: &GetCacheQueryCond,
171
        value: Option<&DeviceRouteCacheDlData>,
172
8
    ) -> Result<(), Box<dyn StdError>> {
173
8
        let key = format!(
174
8
            "{}.{}.{}",
175
8
            cond.unit_code, cond.network_code, cond.network_addr
176
8
        );
177
8
        let mut lock = self.dldata.write().await;
178
8
        let _ = match value {
179
2
            None => lock.push(key, None),
180
6
            Some(value) => lock.push(key, Some(value.clone())),
181
        };
182
8
        Ok(())
183
16
    }
184

            
185
16466
    async fn del_dldata(&self, cond: &DelCacheQueryCond) -> Result<(), Box<dyn StdError>> {
186
16466
        let key = match cond.network_code {
187
            None => {
188
                // Remove all routes of the unit.
189
8
                cond.unit_code.to_string()
190
            }
191
16458
            Some(code) => match cond.network_addr {
192
                None => {
193
                    // Remove all routes of the network.
194
1
                    format!("{}.{}", cond.unit_code, code)
195
                }
196
16457
                Some(addr) => {
197
16457
                    let key = format!("{}.{}.{}", cond.unit_code, code, addr);
198
16457
                    let mut lock = self.dldata.write().await;
199
16457
                    let _ = lock.pop(&key);
200
16457
                    return Ok(());
201
                }
202
            },
203
        };
204
        {
205
9
            let mut lock = self.dldata.write().await;
206
            loop {
207
11
                let mut rm_key = None;
208
11
                for (k, _) in lock.iter() {
209
3
                    if k.starts_with(key.as_str()) {
210
2
                        rm_key = Some(k.clone());
211
2
                        break;
212
1
                    }
213
                }
214
11
                match rm_key {
215
9
                    None => break,
216
2
                    Some(key) => {
217
2
                        let _ = lock.pop(&key);
218
2
                    }
219
                }
220
            }
221
        }
222
9
        Ok(())
223
32932
    }
224

            
225
    async fn get_dldata_pub(
226
        &self,
227
        cond: &GetCachePubQueryCond,
228
11
    ) -> Result<Option<DeviceRouteCacheDlData>, Box<dyn StdError>> {
229
11
        let key = format!("{}.{}", cond.unit_id, cond.device_id);
230

            
231
        {
232
11
            let mut lock = self.dldata_pub.write().await;
233
11
            if let Some(value) = lock.get(&key) {
234
3
                match value {
235
1
                    None => return Ok(None),
236
2
                    Some(value) => return Ok(Some(value.clone())),
237
                }
238
8
            }
239
8
        }
240
8

            
241
8
        let dev_cond = DeviceQueryCond {
242
8
            unit_id: Some(cond.unit_id),
243
8
            device_id: Some(cond.device_id),
244
8
            ..Default::default()
245
8
        };
246
8
        let device = self.model.device().get(&dev_cond).await?;
247
8
        let data = match device {
248
1
            None => None,
249
7
            Some(device) => match device.unit_code.as_ref() {
250
3
                None => Some(DeviceRouteCacheDlData {
251
3
                    net_mgr_key: format!(".{}", device.network_code),
252
3
                    network_id: device.network_id,
253
3
                    network_addr: device.network_addr,
254
3
                    device_id: device.device_id,
255
3
                    profile: device.profile,
256
3
                }),
257
4
                Some(unit_code) => Some(DeviceRouteCacheDlData {
258
4
                    net_mgr_key: format!("{}.{}", unit_code, device.network_code),
259
4
                    network_id: device.network_id,
260
4
                    network_addr: device.network_addr,
261
4
                    device_id: device.device_id,
262
4
                    profile: device.profile,
263
4
                }),
264
            },
265
        };
266
8
        let _ = self.set_dldata_pub(cond, data.as_ref()).await;
267
8
        Ok(data)
268
22
    }
269

            
270
    async fn set_dldata_pub(
271
        &self,
272
        cond: &GetCachePubQueryCond,
273
        value: Option<&DeviceRouteCacheDlData>,
274
8
    ) -> Result<(), Box<dyn StdError>> {
275
8
        let key = format!("{}.{}", cond.unit_id, cond.device_id);
276
8
        let mut lock = self.dldata_pub.write().await;
277
8
        let _ = match value {
278
1
            None => lock.push(key, None),
279
7
            Some(value) => lock.push(key, Some(value.clone())),
280
        };
281
8
        Ok(())
282
16
    }
283

            
284
16465
    async fn del_dldata_pub(&self, cond: &DelCachePubQueryCond) -> Result<(), Box<dyn StdError>> {
285
16465
        let key = match cond.device_id {
286
            None => {
287
                // Remove all routes of the unit.
288
8
                cond.unit_id.to_string()
289
            }
290
16457
            Some(id) => {
291
16457
                let key = format!("{}.{}", cond.unit_id, id);
292
                {
293
16457
                    let mut lock = self.dldata_pub.write().await;
294
16457
                    lock.pop(&key);
295
16457
                }
296
16457
                return Ok(());
297
            }
298
        };
299
        {
300
8
            let mut lock = self.dldata_pub.write().await;
301
            loop {
302
10
                let mut rm_key = None;
303
10
                for (k, _) in lock.iter() {
304
2
                    if k.starts_with(key.as_str()) {
305
2
                        rm_key = Some(k.clone());
306
2
                        break;
307
                    }
308
                }
309
10
                match rm_key {
310
8
                    None => break,
311
2
                    Some(key) => {
312
2
                        let _ = lock.pop(&key);
313
2
                    }
314
                }
315
            }
316
        }
317
8
        Ok(())
318
32930
    }
319
}
320

            
321
impl Default for Options {
322
5
    fn default() -> Self {
323
5
        Options {
324
5
            uldata_size: DEF_SIZE,
325
5
            dldata_size: DEF_SIZE,
326
5
            dldata_pub_size: DEF_SIZE,
327
5
        }
328
5
    }
329
}