1 /* $Id: cdd_client.cpp 636069 2021-08-16 12:14:29Z ivanov $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Aleksey Grichenko
27  *
28  * File Description:
29  *   RPC client for CDD annotations
30  *
31  */
32 
33 #include <ncbi_pch.hpp>
34 #include <serial/objostrjson.hpp>
35 #include <objects/seqloc/Seq_id.hpp>
36 #include <objects/seq/Seq_annot.hpp>
37 #include <objects/id2/ID2_Blob_Id.hpp>
38 #include <objtools/data_loaders/cdd/cdd_access/CDD_Request.hpp>
39 #include <objtools/data_loaders/cdd/cdd_access/cdd_client.hpp>
40 #include <objtools/data_loaders/cdd/cdd_access/cdd_access__.hpp>
41 
42 BEGIN_NCBI_SCOPE
43 
44 NCBI_PARAM_ENUM_DECL(objects::CCDDClient::EDataFormat, CDD, data_format);
NCBI_PARAM_ENUM_ARRAY(objects::CCDDClient::EDataFormat,CDD,data_format)45 NCBI_PARAM_ENUM_ARRAY(objects::CCDDClient::EDataFormat, CDD, data_format)
46 {
47     // Deliberately not covering eDefaultFormat!
48     { "JSON",        objects::CCDDClient::eJSON       },
49     { "semi-binary", objects::CCDDClient::eSemiBinary },
50     { "binary",      objects::CCDDClient::eBinary     }
51 };
52 
53 NCBI_PARAM_ENUM_DEF(objects::CCDDClient::EDataFormat, CDD, data_format,
54                     objects::CCDDClient::eBinary);
55 
56 typedef NCBI_PARAM_TYPE(CDD, data_format) TDataFormatParam;
57 
58 BEGIN_objects_SCOPE // namespace ncbi::objects::
59 
60 static
s_GetSerialFormat(CCDDClient::EDataFormat & data_format)61 ESerialDataFormat s_GetSerialFormat(CCDDClient::EDataFormat& data_format)
62 {
63     if (data_format == CCDDClient::eDefaultFormat) {
64         data_format = TDataFormatParam::GetDefault();
65     }
66     return data_format == CCDDClient::eJSON ? eSerial_Json : eSerial_AsnBinary;
67 }
68 
CCDDClient(const string & service_name,EDataFormat data_format)69 CCDDClient::CCDDClient(const string& service_name, EDataFormat data_format)
70     : Tparent(service_name.empty() ? DEFAULT_CDD_SERVICE_NAME : service_name,
71               s_GetSerialFormat(data_format)),
72       m_DataFormat(data_format)
73 {
74     if (data_format == eBinary) {
75         SetArgs("binary=1");
76     }
77 }
78 
79 
~CCDDClient(void)80 CCDDClient::~CCDDClient(void)
81 {
82 }
83 
84 
Ask(const CCDD_Request_Packet & request,CCDD_Reply & reply)85 void CCDDClient::Ask(const CCDD_Request_Packet& request, CCDD_Reply& reply)
86 {
87     m_Replies.clear();
88     Tparent::Ask(request, reply);
89 }
90 
91 
WriteRequest(CObjectOStream & out,const CCDD_Request_Packet & request)92 void CCDDClient::WriteRequest(CObjectOStream& out,
93                               const CCDD_Request_Packet& request)
94 {
95     static const TSerial_Format_Flags kJSONFlags
96         = fSerial_Json_NoIndentation | fSerial_Json_NoEol;
97     if (m_DataFormat == eSemiBinary) {
98         CNcbiOstrstream oss;
99         CObjectOStreamJson joos(oss, eNoOwnership);
100         joos.SetFormattingFlags(kJSONFlags);
101         joos << request;
102         string s = CNcbiOstrstreamToString(oss);
103         SetArgs("binary=1&requestPacket="
104                 + NStr::URLEncode(s, NStr::eUrlEnc_URIQueryValue));
105         x_Connect();
106     } else {
107         if (m_DataFormat == eJSON) {
108             out.SetFormattingFlags(kJSONFlags);
109         }
110         Tparent::WriteRequest(out, request);
111     }
112 }
113 
114 
ReadReply(CObjectIStream & in,CCDD_Reply & reply)115 void CCDDClient::ReadReply(CObjectIStream& in, CCDD_Reply& reply)
116 {
117     m_Replies.clear();
118     CRef<CCDD_Reply> next_reply;
119     do {
120         next_reply.Reset(new CCDD_Reply);
121         in >> *next_reply;
122         m_Replies.push_back(next_reply);
123     } while (!next_reply->IsSetEnd_of_reply());
124 
125     if (!m_Replies.empty()) {
126         reply.Assign(*m_Replies.back());
127     }
128 }
129 
130 
AskBlobId(int serial_number,const CSeq_id & seq_id)131 CRef<CCDD_Reply> CCDDClient::AskBlobId(int serial_number, const CSeq_id& seq_id)
132 {
133     CCDD_Request_Packet cdd_packet;
134     CRef<CCDD_Request> cdd_request(new CCDD_Request);
135     cdd_request->SetSerial_number(serial_number);
136     cdd_request->SetRequest().SetGet_blob_id().Assign(seq_id);
137     cdd_packet.Set().push_back(cdd_request);
138     CRef<CCDD_Reply> cdd_reply(new CCDD_Reply);
139     try {
140         Ask(cdd_packet, *cdd_reply);
141     }
142     catch (exception& e) {
143         ERR_POST(e.what());
144         return null;
145     }
146     return cdd_reply;
147 }
148 
149 
AskBlob(int serial_number,const CID2_Blob_Id & blob_id)150 CRef<CCDD_Reply> CCDDClient::AskBlob(int serial_number, const CID2_Blob_Id& blob_id)
151 {
152     CCDD_Request_Packet cdd_packet;
153     CRef<CCDD_Request> cdd_request(new CCDD_Request);
154     cdd_request->SetSerial_number(serial_number);
155     cdd_request->SetRequest().SetGet_blob().Assign(blob_id);
156     cdd_packet.Set().push_back(cdd_request);
157     CRef<CCDD_Reply> cdd_reply(new CCDD_Reply);
158     try {
159         Ask(cdd_packet, *cdd_reply);
160     }
161     catch (exception& e) {
162         ERR_POST(e.what());
163         return null;
164     }
165     return cdd_reply;
166 }
167 
168 
JustAsk(const CCDD_Request_Packet & request)169 void CCDDClient::JustAsk(const CCDD_Request_Packet& request)
170 {
171     static const STimeout kZeroTimeout = { 0, 0 };
172     Connect();
173     WriteRequest(*m_Out, request);
174     dynamic_cast<CConn_ServiceStream&>(*m_Stream).Fetch(&kZeroTimeout);
175 }
176 
177 
178 /////////////////////////////////////////////////////////////////////////////
179 // CCDDBlobCache
180 /////////////////////////////////////////////////////////////////////////////
181 
182 
183 const int kMaxCacheLifespanSeconds = 300;
184 const size_t kMaxCacheSize = 1000;
185 
186 struct SCDDCacheInfo
187 {
188     typedef CCDDClientPool::SCDDBlob TBlob;
189 
SCDDCacheInfoSCDDCacheInfo190     SCDDCacheInfo(const CSeq_id_Handle idh, const TBlob& b)
191         : id(idh), blob(b), deadline(kMaxCacheLifespanSeconds) {}
192 
193     CSeq_id_Handle id;
194     TBlob blob;
195     CDeadline deadline;
196 
197 private:
198     SCDDCacheInfo(const SCDDCacheInfo&);
199     SCDDCacheInfo& operator=(const SCDDCacheInfo&);
200 };
201 
202 
203 class CCDDBlobCache
204 {
205 public:
CCDDBlobCache(void)206     CCDDBlobCache(void) {}
~CCDDBlobCache(void)207     ~CCDDBlobCache(void) {}
208 
209     typedef CCDDClientPool::TBlobId TBlobId;
210     typedef CCDDClientPool::SCDDBlob TBlob;
211 
212     TBlob Get(const CSeq_id_Handle& seq_id);
213     TBlob Get(const TBlobId& blob_id);
214     void Add(TBlob blob);
215 
216 private:
217     void x_UpdateDeadline(shared_ptr<SCDDCacheInfo>& info);
218     typedef map<CSeq_id_Handle, shared_ptr<SCDDCacheInfo> > TSeqIdMap;
219     typedef map <string, shared_ptr<SCDDCacheInfo>> TBlobIdMap;
220     typedef list<shared_ptr<SCDDCacheInfo>> TInfoQueue;
221 
222     mutable CFastMutex m_Mutex;
223     TSeqIdMap m_SeqIdMap;
224     TBlobIdMap m_BlobIdMap;
225     TInfoQueue m_Infos;
226 };
227 
228 
Get(const CSeq_id_Handle & seq_id)229 CCDDBlobCache::TBlob CCDDBlobCache::Get(const CSeq_id_Handle& seq_id)
230 {
231     CFastMutexGuard guard(m_Mutex);
232     auto found = m_SeqIdMap.find(seq_id);
233     if (found == m_SeqIdMap.end()) return TBlob();
234     shared_ptr<SCDDCacheInfo> info = found->second;
235     x_UpdateDeadline(info);
236     return info->blob;
237 }
238 
239 
Get(const TBlobId & blob_id)240 CCDDBlobCache::TBlob CCDDBlobCache::Get(const TBlobId& blob_id)
241 {
242     CFastMutexGuard guard(m_Mutex);
243     string sid = CCDDClientPool::BlobIdToString(blob_id);
244     auto found = m_BlobIdMap.find(sid);
245     if (found == m_BlobIdMap.end()) return TBlob();
246     shared_ptr<SCDDCacheInfo> info = found->second;
247     x_UpdateDeadline(info);
248     return info->blob;
249 }
250 
251 
Add(TBlob blob)252 void CCDDBlobCache::Add(TBlob blob)
253 {
254     CSeq_id_Handle idh = CSeq_id_Handle::GetHandle(blob.info->GetSeq_id());
255     CFastMutexGuard guard(m_Mutex);
256     auto found = m_SeqIdMap.find(idh);
257     if (found != m_SeqIdMap.end()) return;
258     // Create new entry.
259     shared_ptr<SCDDCacheInfo> info = make_shared<SCDDCacheInfo>(idh, blob);
260     while (!m_Infos.empty() && (m_Infos.size() > kMaxCacheSize || m_Infos.front()->deadline.IsExpired())) {
261         auto rm = m_Infos.front();
262         m_Infos.pop_front();
263         m_SeqIdMap.erase(rm->id);
264         m_BlobIdMap.erase(CCDDClientPool::BlobIdToString(rm->blob.info->GetBlob_id()));
265     }
266     m_Infos.push_back(info);
267     m_SeqIdMap[idh] = info;
268     m_BlobIdMap[CCDDClientPool::BlobIdToString(info->blob.info->GetBlob_id())] = info;
269 }
270 
271 
x_UpdateDeadline(shared_ptr<SCDDCacheInfo> & info)272 void CCDDBlobCache::x_UpdateDeadline(shared_ptr<SCDDCacheInfo>& info)
273 {
274     m_Infos.remove(info);
275     info->deadline = CDeadline(kMaxCacheLifespanSeconds);
276     m_Infos.push_back(info);
277 }
278 
279 
280 /////////////////////////////////////////////////////////////////////////////
281 // CCDDClientGuard
282 /////////////////////////////////////////////////////////////////////////////
283 
284 
285 class CCDDClientGuard
286 {
287 public:
CCDDClientGuard(CCDDClientPool & pool)288     CCDDClientGuard(CCDDClientPool& pool)
289         : m_Pool(pool)
290     {
291         m_Client = m_Pool.x_GetClient();
292     }
293 
~CCDDClientGuard(void)294     ~CCDDClientGuard(void) {
295         m_Pool.x_ReleaseClient(m_Client);
296     }
297 
Get(void)298     CCDDClient& Get(void) {
299         return *m_Client->second;
300     }
301 
Discard(void)302     void Discard(void)
303     {
304         m_Pool.x_DiscardClient(m_Client);
305     }
306 
307 private:
308     CCDDClientPool&         m_Pool;
309     CCDDClientPool::TClient m_Client;
310 };
311 
312 
313 /////////////////////////////////////////////////////////////////////////////
314 // CCDDClientPool
315 /////////////////////////////////////////////////////////////////////////////
316 
317 
x_NextSerialNumber(void)318 int CCDDClientPool::x_NextSerialNumber(void)
319 {
320     static CAtomicCounter_WithAutoInit s_Counter;
321     return s_Counter.Add(1);
322 }
323 
324 
CCDDClientPool(const string & service_name,size_t pool_soft_limit,time_t pool_age_limit,bool exclude_nucleotides)325 CCDDClientPool::CCDDClientPool(const string& service_name,
326     size_t pool_soft_limit,
327     time_t pool_age_limit,
328     bool exclude_nucleotides)
329 {
330     m_ServiceName = service_name;
331     m_PoolSoftLimit = pool_soft_limit;
332     m_PoolAgeLimit = pool_age_limit;
333     m_ExcludeNucleotides = exclude_nucleotides;
334     m_Cache.reset(new CCDDBlobCache);
335 }
336 
337 
~CCDDClientPool(void)338 CCDDClientPool::~CCDDClientPool(void)
339 {
340 }
341 
342 
GetBlobBySeq_id(CSeq_id_Handle idh)343 CCDDClientPool::SCDDBlob CCDDClientPool::GetBlobBySeq_id(CSeq_id_Handle idh)
344 {
345     SCDDBlob ret = m_Cache->Get(idh);
346     if (ret.data) return ret;
347 
348     if (ret.info) {
349         // Have blob info only, request blob data.
350         ret.data = x_RequestBlobData(ret.info->GetBlob_id());
351         if (ret.data) {
352             m_Cache->Add(ret);
353         }
354         return ret;
355     }
356 
357     // Make a blob-by-seq-id request.
358     int serial = x_NextSerialNumber();
359     CCDD_Request_Packet cdd_packet;
360     CRef<CCDD_Request> cdd_request(new CCDD_Request);
361     cdd_request->SetSerial_number(serial);
362 
363 
364     CConstRef<CSeq_id> id(idh.GetSeqId());
365     if (!IsValidId(*id)) return ret;
366     CRef<CSeq_id> nc_id(new CSeq_id);
367     nc_id->Assign(*id);
368     cdd_request->SetRequest().SetGet_blob_by_seq_id(*nc_id);
369     cdd_packet.Set().push_back(cdd_request);
370 
371     CCDDClientGuard client(*this);
372     CRef<CCDD_Reply> cdd_reply(new CCDD_Reply);
373     try {
374         client.Get().Ask(cdd_packet, *cdd_reply);
375         if (!x_CheckReply(cdd_reply, serial, CCDD_Reply::TReply::e_Get_blob_by_seq_id)) {
376             return ret;
377         }
378         auto& cdd_blob = cdd_reply->SetReply().SetGet_blob_by_seq_id();
379         ret.info.Reset(&cdd_blob.SetBlob_id());
380         ret.data.Reset(&cdd_blob.SetBlob());
381         m_Cache->Add(ret);
382     }
383     catch (exception& e) {
384         ERR_POST("CDD - get-blob-by-seq-id request failed: " << e.what());
385         client.Discard();
386     }
387     catch (...) {
388         client.Discard();
389     }
390     return ret;
391 }
392 
393 
GetBlobBySeq_ids(const TSeq_idSet & ids)394 CCDDClientPool::SCDDBlob CCDDClientPool::GetBlobBySeq_ids(const TSeq_idSet& ids)
395 {
396     SCDDBlob ret;
397     ITERATE(TSeq_idSet, id_it, ids) {
398         ret = m_Cache->Get(*id_it);
399         if (ret.info) break;
400     }
401 
402     if (ret.data) return ret;
403 
404     if (ret.info) {
405         // Have blob info only, request blob data.
406         ret.data = x_RequestBlobData(ret.info->GetBlob_id());
407         if (ret.data) {
408             m_Cache->Add(ret);
409         }
410         return ret;
411     }
412 
413     // Make a blob-by-seq-id request.
414     int serial = x_NextSerialNumber();
415     CCDD_Request_Packet cdd_packet;
416     CRef<CCDD_Request> cdd_request(new CCDD_Request);
417     cdd_request->SetSerial_number(serial);
418 
419     list<CRef<CSeq_id>>& req_ids = cdd_request->SetRequest().SetGet_blob_by_seq_ids();
420     ITERATE(TSeq_idSet, id_it, ids) {
421         CConstRef<CSeq_id> id(id_it->GetSeqId());
422         if (!IsValidId(*id)) continue;
423         CRef<CSeq_id> nc_id(new CSeq_id);
424         nc_id->Assign(*id);
425         req_ids.push_back(nc_id);
426     }
427     if (req_ids.empty()) return ret;
428     cdd_packet.Set().push_back(cdd_request);
429 
430     CCDDClientGuard client(*this);
431     CRef<CCDD_Reply> cdd_reply(new CCDD_Reply);
432     try {
433         client.Get().Ask(cdd_packet, *cdd_reply);
434         if (!x_CheckReply(cdd_reply, serial, CCDD_Reply::TReply::e_Get_blob_by_seq_id)) {
435             return ret;
436         }
437         auto& cdd_blob = cdd_reply->SetReply().SetGet_blob_by_seq_id();
438         ret.info.Reset(&cdd_blob.SetBlob_id());
439         ret.data.Reset(&cdd_blob.SetBlob());
440         m_Cache->Add(ret);
441     }
442     catch (exception& e) {
443         ERR_POST("CDD - get-blob-by-seq-ids request failed: " << e.what());
444         client.Discard();
445     }
446     catch (...) {
447         client.Discard();
448     }
449     return ret;
450 }
451 
452 
GetBlobIdBySeq_id(CSeq_id_Handle idh)453 CCDDClientPool::TBlobInfo CCDDClientPool::GetBlobIdBySeq_id(CSeq_id_Handle idh)
454 {
455     SCDDBlob blob = m_Cache->Get(idh);
456     if (blob.info) return blob.info;
457 
458     int serial = x_NextSerialNumber();
459     CCDD_Request_Packet cdd_packet;
460     CRef<CCDD_Request> cdd_request(new CCDD_Request);
461     cdd_request->SetSerial_number(serial);
462 
463     CConstRef<CSeq_id> id(idh.GetSeqId());
464     if (!IsValidId(*id)) return blob.info;
465 
466     cdd_request->SetRequest().SetGet_blob_id().Assign(*id);
467     cdd_packet.Set().push_back(cdd_request);
468 
469     CCDDClientGuard client(*this);
470     CRef<CCDD_Reply> cdd_reply(new CCDD_Reply);
471     try {
472         client.Get().Ask(cdd_packet, *cdd_reply);
473         if (!x_CheckReply(cdd_reply, serial, CCDD_Reply::TReply::e_Get_blob_id)) {
474             return blob.info;
475         }
476         blob.info.Reset(&cdd_reply->SetReply().SetGet_blob_id());
477         m_Cache->Add(blob);
478     }
479     catch (exception& e) {
480         ERR_POST("CDD - get-blob-id request failed: " << e.what());
481         client.Discard();
482     }
483     catch (...) {
484         client.Discard();
485     }
486     return blob.info;
487 }
488 
489 
GetBlobByBlobId(const TBlobId & blob_id)490 CCDDClientPool::TBlobData CCDDClientPool::GetBlobByBlobId(const TBlobId& blob_id)
491 {
492     SCDDBlob blob = m_Cache->Get(blob_id);
493     if (blob.data) return blob.data;
494 
495     blob.data = x_RequestBlobData(blob_id);
496     if (blob.info && blob.data) {
497         m_Cache->Add(blob);
498     }
499     return blob.data;
500 }
501 
502 
x_RequestBlobData(const TBlobId & blob_id)503 CCDDClientPool::TBlobData CCDDClientPool::x_RequestBlobData(const TBlobId& blob_id)
504 {
505     TBlobData ret;
506     int serial = x_NextSerialNumber();
507     CCDD_Request_Packet cdd_packet;
508     CRef<CCDD_Request> cdd_request(new CCDD_Request);
509     cdd_request->SetSerial_number(serial);
510 
511     auto& req = cdd_request->SetRequest().SetGet_blob();
512     req.SetSat(blob_id.GetSat());
513     req.SetSub_sat(blob_id.GetSub_sat());
514     req.SetSat_key(blob_id.GetSat_key());
515     cdd_packet.Set().push_back(cdd_request);
516 
517     CCDDClientGuard client(*this);
518     CRef<CCDD_Reply> cdd_reply(new CCDD_Reply);
519     try {
520         client.Get().Ask(cdd_packet, *cdd_reply);
521         if (!x_CheckReply(cdd_reply, serial, CCDD_Reply::TReply::e_Get_blob)) {
522             return ret;
523         }
524         ret.Reset(&cdd_reply->SetReply().SetGet_blob());
525     }
526     catch (exception& e) {
527         ERR_POST("CDD - get-blob request failed: " << e.what());
528         client.Discard();
529     }
530     catch (...) {
531         client.Discard();
532     }
533     return ret;
534 }
535 
536 
x_CheckReply(CRef<CCDD_Reply> & reply,int serial,CCDD_Reply::TReply::E_Choice choice)537 bool CCDDClientPool::x_CheckReply(CRef<CCDD_Reply>& reply, int serial, CCDD_Reply::TReply::E_Choice choice)
538 {
539     if (!reply) return false;
540     if (reply->GetReply().IsEmpty() && !reply->IsSetError()) return false;
541     if (reply->IsSetError()) {
542         const CCDD_Error& e = reply->GetError();
543         ERR_POST("CDD - reply error: " << e.GetMessage() << " (code " << e.GetCode() << ", severity " << (int)e.GetSeverity() << ").");
544         return false;
545     }
546     if (reply->GetSerial_number() != serial) {
547         ERR_POST("CDD - serial number mismatch: " << serial << " != " << reply->GetSerial_number());
548         return false;
549     }
550     if (reply->GetReply().Which() != choice) {
551         ERR_POST("CDD - wrong reply type: " << reply->GetReply().Which() << " != " << choice);
552         return false;
553     }
554     return true;
555 }
556 
557 
IsValidId(const CSeq_id & id)558 bool CCDDClientPool::IsValidId(const CSeq_id& id)
559 {
560     switch (id.Which()) {
561     case CSeq_id::e_not_set:
562     case CSeq_id::e_Local:
563     case CSeq_id::e_Gibbsq:
564     case CSeq_id::e_Gibbmt:
565     case CSeq_id::e_Giim:
566     case CSeq_id::e_Patent:
567     case CSeq_id::e_General:
568     case CSeq_id::e_Gpipe:
569     case CSeq_id::e_Named_annot_track:
570         // These seq-ids are not used in CDD.
571         return false;
572     case CSeq_id::e_Gi:
573     case CSeq_id::e_Pdb:
574         // Non-text seq-ids present in CDD.
575         return true;
576     default:
577         break;
578     }
579     // For text seq-ids check accession type.
580     if (m_ExcludeNucleotides && (id.IdentifyAccession() & CSeq_id::fAcc_nuc) != 0) return false;
581     return true;
582 }
583 
584 
x_GetClient()585 CCDDClientPool::TClient CCDDClientPool::x_GetClient()
586 {
587     TClientPool::iterator ret = m_InUse.end();
588     time_t now;
589     CTime::GetCurrentTimeT(&now);
590     time_t cutoff = now - m_PoolAgeLimit;
591     CFastMutexGuard guard(m_PoolLock);
592     TClientPool::iterator it = m_NotInUse.lower_bound(cutoff);
593     if (it == m_NotInUse.end()) {
594         CRef<CCDDClient> client(new CCDDClient(m_ServiceName));
595         ret = m_InUse.emplace(now, client);
596     }
597     else {
598         ret = m_InUse.insert(*it);
599         ++it;
600     }
601     m_NotInUse.erase(m_NotInUse.begin(), it);
602     return ret;
603 }
604 
605 
x_ReleaseClient(TClientPool::iterator & client)606 void CCDDClientPool::x_ReleaseClient(TClientPool::iterator& client)
607 {
608     time_t now;
609     CTime::GetCurrentTimeT(&now);
610     time_t cutoff = now - m_PoolAgeLimit;
611     CFastMutexGuard guard(m_PoolLock);
612     m_NotInUse.erase(m_NotInUse.begin(), m_NotInUse.lower_bound(cutoff));
613     if (client != m_InUse.end()) {
614         if (client->first >= cutoff
615             && m_InUse.size() + m_NotInUse.size() <= m_PoolSoftLimit) {
616             m_NotInUse.insert(*client);
617         }
618         m_InUse.erase(client);
619         client = m_InUse.end();
620     }
621 }
622 
623 
x_DiscardClient(TClient & client)624 void CCDDClientPool::x_DiscardClient(TClient& client)
625 {
626     CFastMutexGuard guard(m_PoolLock);
627     m_InUse.erase(client);
628     client = m_InUse.end();
629 }
630 
631 
BlobIdToString(const TBlobId & blob_id)632 string CCDDClientPool::BlobIdToString(const TBlobId& blob_id)
633 {
634     ostringstream s;
635     s << blob_id.GetSat() << '/' << blob_id.GetSub_sat() << '.' << blob_id.GetSat_key();
636     return s.str();
637 }
638 
StringToBlobId(const string & s)639 CRef<CCDDClientPool::TBlobId> CCDDClientPool::StringToBlobId(const string& s)
640 {
641     CRef<TBlobId> ret;
642     try {
643         vector<string> parts;
644         NStr::Split(s, "/.", parts);
645         if (parts.size() != 3) return ret;
646         CRef<TBlobId> blob_id(new TBlobId);
647         blob_id->SetSat(NStr::StringToNumeric<TBlobId::TSat>(parts[0]));
648         blob_id->SetSub_sat(NStr::StringToNumeric<TBlobId::TSat>(parts[1]));
649         blob_id->SetSat_key(NStr::StringToNumeric<TBlobId::TSat>(parts[2]));
650         ret = blob_id;
651     }
652     catch (...) {}
653     return ret;
654 }
655 
656 
657 END_objects_SCOPE // namespace ncbi::objects::
658 
659 END_NCBI_SCOPE
660 
661 /* Original file checksum: lines: 57, chars: 1749, CRC32: ab618a22 */
662