1- use std:: sync:: Arc ;
1+ use std:: {
2+ collections:: { HashMap , VecDeque } ,
3+ sync:: { Arc , Mutex } ,
4+ } ;
25
36use oai_pmh:: core:: MetadataPrefix ;
47use quick_xml:: { events:: Event , Reader , Writer } ;
@@ -18,13 +21,49 @@ pub(crate) const PAGE_LIMIT: i64 = 50;
1821const OAI_DC_SPEC : & str = "dublin_core::thoth" ;
1922const OAI_OPENAIRE_SPEC : & str = "openaire::thoth" ;
2023const MARCXML_SPEC : & str = "marc21xml::thoth" ;
24+ const DELEGATED_RECORD_CACHE_LIMIT : usize = 2048 ;
25+ type DelegatedRecordCacheKey = ( Uuid , & ' static str ) ;
26+
27+ #[ derive( Default ) ]
28+ struct DelegatedRecordCache {
29+ entries : HashMap < DelegatedRecordCacheKey , String > ,
30+ insertion_order : VecDeque < DelegatedRecordCacheKey > ,
31+ }
32+
33+ impl DelegatedRecordCache {
34+ fn get ( & self , key : & DelegatedRecordCacheKey ) -> Option < String > {
35+ self . entries . get ( key) . cloned ( )
36+ }
37+
38+ fn insert ( & mut self , key : DelegatedRecordCacheKey , value : String ) {
39+ match self . entries . entry ( key) {
40+ std:: collections:: hash_map:: Entry :: Occupied ( mut entry) => {
41+ entry. insert ( value) ;
42+ return ;
43+ }
44+ std:: collections:: hash_map:: Entry :: Vacant ( entry) => {
45+ self . insertion_order . push_back ( * entry. key ( ) ) ;
46+ entry. insert ( value) ;
47+ }
48+ }
49+
50+ while self . entries . len ( ) > DELEGATED_RECORD_CACHE_LIMIT {
51+ if let Some ( oldest_key) = self . insertion_order . pop_front ( ) {
52+ self . entries . remove ( & oldest_key) ;
53+ } else {
54+ break ;
55+ }
56+ }
57+ }
58+ }
2159
2260#[ derive( Clone ) ]
2361pub ( crate ) struct OaiService {
2462 public_url : String ,
2563 export_url : String ,
2664 thoth_client : Arc < ThothClient > ,
2765 export_client : Client ,
66+ delegated_record_cache : Arc < Mutex < DelegatedRecordCache > > ,
2867}
2968
3069#[ derive( Debug , Clone ) ]
@@ -41,6 +80,7 @@ impl OaiService {
4180 export_url,
4281 thoth_client : Arc :: new ( ThothClient :: new ( gql_endpoint) ) ,
4382 export_client : Client :: new ( ) ,
83+ delegated_record_cache : Arc :: new ( Mutex :: new ( DelegatedRecordCache :: default ( ) ) ) ,
4484 }
4585 }
4686
@@ -104,17 +144,35 @@ impl OaiService {
104144 }
105145
106146 pub ( crate ) async fn get_marcxml_record ( & self , work_id : Uuid ) -> ThothResult < String > {
107- self . get_delegated_record ( work_id, MARCXML_SPEC , b"record" , "MARCXML" )
147+ self . get_delegated_record (
148+ work_id,
149+ MetadataPrefix :: MarcXml ,
150+ MARCXML_SPEC ,
151+ b"record" ,
152+ "MARCXML" ,
153+ )
108154 . await
109155 }
110156
111157 pub ( crate ) async fn get_oai_dc_record ( & self , work_id : Uuid ) -> ThothResult < String > {
112- self . get_delegated_record ( work_id, OAI_DC_SPEC , b"dc" , "Dublin Core" )
158+ self . get_delegated_record (
159+ work_id,
160+ MetadataPrefix :: OaiDc ,
161+ OAI_DC_SPEC ,
162+ b"dc" ,
163+ "Dublin Core" ,
164+ )
113165 . await
114166 }
115167
116168 pub ( crate ) async fn get_oai_openaire_record ( & self , work_id : Uuid ) -> ThothResult < String > {
117- self . get_delegated_record ( work_id, OAI_OPENAIRE_SPEC , b"resource" , "OpenAIRE" )
169+ self . get_delegated_record (
170+ work_id,
171+ MetadataPrefix :: OaiOpenaire ,
172+ OAI_OPENAIRE_SPEC ,
173+ b"resource" ,
174+ "OpenAIRE" ,
175+ )
118176 . await
119177 }
120178
@@ -139,10 +197,16 @@ impl OaiService {
139197 async fn get_delegated_record (
140198 & self ,
141199 work_id : Uuid ,
200+ metadata_prefix : MetadataPrefix ,
142201 specification : & str ,
143202 element_local_name : & [ u8 ] ,
144203 format_name : & str ,
145204 ) -> ThothResult < String > {
205+ let cache_key = ( work_id, metadata_prefix. as_str ( ) ) ;
206+ if let Some ( record) = self . get_cached_delegated_record ( & cache_key) {
207+ return Ok ( record) ;
208+ }
209+
146210 let response = self
147211 . export_client
148212 . get ( format ! (
@@ -168,7 +232,22 @@ impl OaiService {
168232 ) ) ) ;
169233 }
170234
171- Self :: extract_xml_element ( & body, element_local_name, format_name)
235+ let record = Self :: extract_xml_element ( & body, element_local_name, format_name) ?;
236+ self . cache_delegated_record ( cache_key, record. clone ( ) ) ;
237+ Ok ( record)
238+ }
239+
240+ fn get_cached_delegated_record ( & self , key : & DelegatedRecordCacheKey ) -> Option < String > {
241+ self . delegated_record_cache
242+ . lock ( )
243+ . ok ( )
244+ . and_then ( |cache| cache. get ( key) )
245+ }
246+
247+ fn cache_delegated_record ( & self , key : DelegatedRecordCacheKey , value : String ) {
248+ if let Ok ( mut cache) = self . delegated_record_cache . lock ( ) {
249+ cache. insert ( key, value) ;
250+ }
172251 }
173252
174253 pub ( crate ) fn oai_identifier ( work_id : Uuid ) -> String {
0 commit comments