1
+ use std:: collections:: BTreeMap ;
1
2
use std:: num:: NonZeroUsize ;
2
- use std:: sync:: Mutex ;
3
+ use std:: sync:: { Arc , Mutex } ;
3
4
4
5
use async_trait:: async_trait;
5
6
use lru:: LruCache ;
7
+ use serde:: { Deserialize , Serialize } ;
6
8
use smallvec:: smallvec;
9
+ use tokio:: sync:: Notify ;
7
10
11
+ use crate :: data:: PluginCache ;
8
12
use crate :: flow:: * ;
9
13
10
14
const CACHE_SIZE : NonZeroUsize = NonZeroUsize :: new ( 1000 ) . unwrap ( ) ;
15
+ const PLUGIN_CACHE_KEY : & str = "map" ;
11
16
12
17
struct Inner {
13
18
current : u16 ,
14
19
cache : LruCache < String , u16 > ,
15
20
}
16
21
22
+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
23
+ struct InnerCache {
24
+ current : u16 ,
25
+ cache : BTreeMap < String , u16 > ,
26
+ }
27
+
17
28
pub struct FakeIp {
18
29
prefix_v4 : u16 ,
19
30
prefix_v6 : [ u8 ; 14 ] ,
20
- inner : Mutex < Inner > ,
31
+ inner : Arc < Mutex < Inner > > ,
32
+ plugin_cache : PluginCache ,
33
+ new_notify : Arc < Notify > ,
21
34
}
22
35
23
36
impl FakeIp {
24
- pub fn new ( prefix_v4 : [ u8 ; 2 ] , prefix_v6 : [ u8 ; 14 ] ) -> Self {
25
- // TODO: persist current cursor into cache
37
+ pub fn new ( prefix_v4 : [ u8 ; 2 ] , prefix_v6 : [ u8 ; 14 ] , plugin_cache : PluginCache ) -> Self {
38
+ let mut lru = LruCache :: new ( CACHE_SIZE ) ;
39
+ let inner = match plugin_cache
40
+ . get :: < InnerCache > ( PLUGIN_CACHE_KEY )
41
+ . ok ( )
42
+ . flatten ( )
43
+ {
44
+ Some ( cache) => {
45
+ for ( k, v) in cache. cache {
46
+ lru. put ( k, v) ;
47
+ }
48
+ Inner {
49
+ current : cache. current ,
50
+ cache : lru,
51
+ }
52
+ }
53
+ None => Inner {
54
+ current : 1 ,
55
+ cache : lru,
56
+ } ,
57
+ } ;
26
58
Self {
27
59
prefix_v4 : u16:: from_be_bytes ( prefix_v4) ,
28
60
prefix_v6,
29
- inner : Mutex :: new ( Inner {
30
- current : 1 ,
31
- cache : LruCache :: new ( CACHE_SIZE ) ,
32
- } ) ,
61
+ inner : Arc :: new ( Mutex :: new ( inner) ) ,
62
+ plugin_cache,
63
+ new_notify : Arc :: new ( Notify :: new ( ) ) ,
33
64
}
34
65
}
35
66
fn lookup_or_alloc ( & self , domain : String ) -> u16 {
36
- let mut inner = self . inner . lock ( ) . unwrap ( ) ;
37
- let cached = inner. cache . get ( & * domain) . copied ( ) ;
38
- if let Some ( cached) = cached {
39
- return cached;
40
- }
41
- let ret = inner. current ;
42
- inner. cache . put ( domain, ret) ;
43
- inner. current += 1 ;
67
+ let ret = {
68
+ let mut inner = self . inner . lock ( ) . unwrap ( ) ;
69
+ let cached = inner. cache . get ( & * domain) . copied ( ) ;
70
+ if let Some ( cached) = cached {
71
+ return cached;
72
+ }
73
+ let ret = inner. current ;
74
+ inner. cache . put ( domain, ret) ;
75
+ inner. current = inner. current . wrapping_add ( 1 ) ;
76
+ ret
77
+ } ;
78
+ self . new_notify . notify_one ( ) ;
44
79
ret
45
80
}
81
+ fn save_cache ( & self ) {
82
+ let cache = {
83
+ let inner = self . inner . lock ( ) . unwrap ( ) ;
84
+ InnerCache {
85
+ current : inner. current ,
86
+ cache : inner. cache . iter ( ) . map ( |( k, v) | ( k. clone ( ) , * v) ) . collect ( ) ,
87
+ }
88
+ } ;
89
+ self . plugin_cache . set ( PLUGIN_CACHE_KEY , & cache) . ok ( ) ;
90
+ }
46
91
}
47
92
48
93
#[ async_trait]
@@ -62,3 +107,43 @@ impl Resolver for FakeIp {
62
107
Ok ( smallvec ! [ bytes. into( ) ] )
63
108
}
64
109
}
110
+
111
+ impl Drop for FakeIp {
112
+ fn drop ( & mut self ) {
113
+ self . save_cache ( ) ;
114
+ }
115
+ }
116
+
117
+ pub async fn cache_writer ( plugin : Arc < FakeIp > ) {
118
+ let ( plugin, notify) = {
119
+ let notify = plugin. new_notify . clone ( ) ;
120
+ let weak = Arc :: downgrade ( & plugin) ;
121
+ drop ( plugin) ;
122
+ ( weak, notify)
123
+ } ;
124
+ if plugin. strong_count ( ) == 0 {
125
+ panic ! ( "fakeip has no strong reference left for cache_writer" ) ;
126
+ }
127
+
128
+ use tokio:: select;
129
+ use tokio:: time:: { sleep, Duration } ;
130
+ loop {
131
+ let mut notified_fut = notify. notified ( ) ;
132
+ let mut sleep_fut = sleep ( Duration :: from_secs ( 3600 ) ) ;
133
+ ' debounce: loop {
134
+ select ! {
135
+ _ = notified_fut => {
136
+ notified_fut = notify. notified( ) ;
137
+ sleep_fut = sleep( Duration :: from_secs( 3 ) ) ;
138
+ }
139
+ _ = sleep_fut => {
140
+ break ' debounce;
141
+ }
142
+ }
143
+ }
144
+ match plugin. upgrade ( ) {
145
+ Some ( plugin) => plugin. save_cache ( ) ,
146
+ None => break ,
147
+ }
148
+ }
149
+ }
0 commit comments