11"""
22ers.api
33
4- Provides class ERS implementing API to Entity Rgistry System.
4+ Provides class ERS implementing API to Entity Registry System.
55
66"""
77import re
88import sys
9- import uuid
109
1110from hashlib import md5
1211from socket import gethostname
12+ from collections import Counter
13+ from random import randrange
1314
1415import store
16+ from timeout import TimeoutError
1517
1618class ERSReadOnly (object ):
1719 """ ERS version with read-only methods.
@@ -26,18 +28,30 @@ def __init__(self,
2628 local_only = False ):
2729 self ._local_only = local_only
2830 self .fixed_peers = [] if self ._local_only else list (fixed_peers )
31+ self ._timeout_count = Counter ()
2932 self .store = store .LocalStore ()
3033 self ._init_host_urn ()
3134
3235 def _init_host_urn (self ):
33- fingerprint = md5 (gethostname ()).hexdigest ()
34- self .host_urn = "urn:ers:host:{}" .format (fingerprint )
36+ # Use uuid provided by CouchDB 1.3+, fallback to hostname fingerprint
37+ try :
38+ uid = self .store .info ()['uuid' ]
39+ except KeyError :
40+ uid = md5 (gethostname ()).hexdigest ()
41+ self .host_urn = "urn:ers:host:{}" .format (uid )
3542
3643 def get_machine_uuid (self ):
3744 '''
3845 @return a unique identifier for this ERS node
3946 '''
40- return str (uuid .getnode ())
47+ return self .host_urn .split (':' )[- 1 ]
48+
49+ def _is_failing (self , url ):
50+ """
51+ Returns True for url's which failed to respond with increasing probability.
52+ Returns False for url's which did not fail.
53+ """
54+ return randrange (self ._timeout_count [url ] + 1 ) != 0
4155
4256 def get_entity (self , entity_name ):
4357 '''
@@ -54,15 +68,23 @@ def get_entity(self, entity_name):
5468 entity .add_document (doc , source )
5569
5670 # Get documents out of public/cache of connected peers
71+ # TODO parallelize
5772 for peer in self .get_peers ():
73+ url = peer ['server_url' ]
74+ if self ._is_failing (url ):
75+ continue
76+
5877 remote_docs = []
5978 try :
60- remote_store = store .RemoteStore (peer ['server_url' ])
61- remote_docs .extend (remote_store .docs_by_entity (entity_name ))
62- except :
63- sys .stderr .write ("Warning: failed to query remote peer {0}\n " .format (peer ))
64- continue
79+ remote_docs = store .query_remote (url , 'docs_by_entity' , entity_name )
80+ except TimeoutError :
81+ self ._timeout_count [url ] += 1
82+ sys .stderr .write ("Incremented timeout count for {0}: {1}\n " .format (
83+ url , self ._timeout_count [url ]))
84+ except Exception as e :
85+ sys .stderr .write ("Warning: failed to query remote peer {0}. Error: {1}\n " .format (peer , e ))
6586 else :
87+ self ._timeout_count .pop (url , 0 )
6688 for doc in remote_docs :
6789 entity .add_document (doc , 'remote' )
6890 return entity
@@ -81,13 +103,21 @@ def search(self, prop, value=None):
81103
82104 # Search peers
83105 for peer in self .get_peers ():
106+ url = peer ['server_url' ]
107+ if self ._is_failing (url ):
108+ continue
109+
84110 remote_result = []
85111 try :
86- remote_store = store .RemoteStore (peer ['server_url' ])
87- remote_result = set (remote_store .by_property_value (prop , value ))
88- except :
89- sys .stderr .write ("Warning: failed to query remote peer {0}\n " .format (peer ))
112+ remote_result = store .query_remote (url , 'by_property_value' , prop , value )
113+ except TimeoutError :
114+ self ._timeout_count [url ] += 1
115+ sys .stderr .write ("Incremented timeout count for {0}: {1}\n " .format (
116+ url , self ._timeout_count [url ]))
117+ except Exception as e :
118+ sys .stderr .write ("Warning: failed to query remote peer {0}. Error: {1}\n " .format (peer , e ))
90119 else :
120+ self ._timeout_count .pop (url , 0 )
91121 result .update (remote_result )
92122
93123 return list (result )
0 commit comments