import socket from random import sample from urlparse import urlparse from couchdb import Server, Session from couchdb.http import ResourceNotFound from time import sleep import sys import logging TRUE = True LOGGER = logging.getLogger(__name__) def couchdb_dns_query_settings(server_url, database_name): parsed_url = urlparse(server_url) all_ips = set([str(i[4][0]) for i in socket.getaddrinfo(urlparse(server_url).hostname, 80)]) while all_ips: selected_ip = set(sample(all_ips, 1)) all_ips -= selected_ip couch_url = server_url.replace(parsed_url.hostname, selected_ip.pop()) try: server = Server(couch_url, session=Session(retry_delays=range(10))) return server[database_name] except socket.error: continue raise Exception("No route to any couchdb server") def iterview(server_url, database_name, view_name, sleep_seconds=10, wrapper=None, **options): """Iterate the rows in a view, fetching rows in batches and yielding one row at a time. Since the view's rows are fetched in batches any rows emitted for documents added, changed or deleted between requests may be missed or repeated. :param name: the name of the view; for custom views, use the format ``design_docid/viewname``, that is, the document ID of the design document and the name of the view, separated by a slash. :param batch: number of rows to fetch per HTTP request. :param wrapper: an optional callable that should be used to wrap the result rows :param options: optional query string parameters :return: row generator """ database = couchdb_dns_query_settings(server_url, database_name) start_key = 0 options['start_key'] = start_key options['limit'] = 1000 design_timeout = 2 # start timeout for view waiting while TRUE: try: rows = list(database.view(view_name, wrapper, **options)) except socket.error: options['start_key'] = 0 database = couchdb_dns_query_settings(server_url, database_name) continue except ResourceNotFound as e: if design_timeout > 16: LOGGER.error('Iterview couch error: {}'.format(repr(e))) raise e LOGGER.warning('Missing view document, waiting...') sleep(design_timeout) design_timeout *= 2 continue except Exception as e: LOGGER.warning('Couch error: {}'.format(repr(e))) raise e if len(rows) != 0: for row in rows: start_key = row['key'] yield row else: sleep(sleep_seconds) options['start_key'] = (start_key + 1)