def connection(self, storage_url): pool = self.conn_pools[storage_url] try: hc = pool.get() try: yield hc except (CannotSendRequest, HTTPConnectionClosed, socket.timeout, socket.error) as e: logging.debug("@connection hit %r...", e) try: hc[1].close() except Exception: pass hc = pool.create() finally: if hc and hc[1] and not hc[1].sock.closed: pool.put(hc) else: pool.put(pool.create())
def _upload_pg_cluster_dir(self, start_backup_info, pg_cluster_dir, version, pool_size, rate_limit=None): """ Upload to url_prefix from pg_cluster_dir This function ignores the directory pg_xlog, which contains WAL files and are not generally part of a base backup. Note that this is also lzo compresses the files: thus, the number of pooled processes involves doing a full sequential scan of the uncompressed Postgres heap file that is pipelined into lzo. Once lzo is completely finished (necessary to have access to the file size) the file is sent to S3 or WABS. TODO: Investigate an optimization to decouple the compression and upload steps to make sure that the most efficient possible use of pipelining of network and disk resources occurs. Right now it possible to bounce back and forth between bottlenecking on reading from the database block device and subsequently the S3/WABS sending steps should the processes be at the same stage of the upload pipeline: this can have a very negative impact on being able to make full use of system resources. Furthermore, it desirable to overflowing the page cache: having separate tunables for number of simultanious compression jobs (which occupy /tmp space and page cache) and number of uploads (which affect upload throughput) would help. """ spec, parts = tar_partition.partition(pg_cluster_dir) # TODO :: Move arbitray path construction to StorageLayout Object backup_prefix = '{0}/basebackups_{1}/base_{file_name}_{file_offset}'\ .format(self.layout.prefix.rstrip('/'), FILE_STRUCTURE_VERSION, **start_backup_info) if rate_limit is None: per_process_limit = None else: per_process_limit = int(rate_limit / pool_size) # Reject tiny per-process rate limits. They should be # rejected more nicely elsewhere. assert per_process_limit > 0 or per_process_limit is None total_size = 0 # Make an attempt to upload extended version metadata extended_version_url = backup_prefix + '/extended_version.txt' logger.info( msg='start upload postgres version metadata', detail=('Uploading to {extended_version_url}.' .format(extended_version_url=extended_version_url))) uri_put_file(self.creds, extended_version_url, StringIO(version), content_encoding='text/plain') logger.info(msg='postgres version metadata upload complete') uploader = PartitionUploader(self.creds, backup_prefix, per_process_limit, self.gpg_key_id) pool = TarUploadPool(uploader, pool_size) # Enqueue uploads for parallel execution for tpart in parts: total_size += tpart.total_member_size # 'put' can raise an exception for a just-failed upload, # aborting the process. pool.put(tpart) # Wait for remaining parts to upload. An exception can be # raised to signal failure of the upload. pool.join() return spec, backup_prefix, total_size
def _upload_pg_cluster_dir(self, start_backup_info, pg_cluster_dir, version, pool_size, rate_limit=None): """ Upload to url_prefix from pg_cluster_dir This function ignores the directory pg_xlog, which contains WAL files and are not generally part of a base backup. Note that this is also lzo compresses the files: thus, the number of pooled processes involves doing a full sequential scan of the uncompressed Postgres heap file that is pipelined into lzo. Once lzo is completely finished (necessary to have access to the file size) the file is sent to S3 or WABS. TODO: Investigate an optimization to decouple the compression and upload steps to make sure that the most efficient possible use of pipelining of network and disk resources occurs. Right now it possible to bounce back and forth between bottlenecking on reading from the database block device and subsequently the S3/WABS sending steps should the processes be at the same stage of the upload pipeline: this can have a very negative impact on being able to make full use of system resources. Furthermore, it desirable to overflowing the page cache: having separate tunables for number of simultanious compression jobs (which occupy /tmp space and page cache) and number of uploads (which affect upload throughput) would help. """ spec, parts = tar_partition.partition(pg_cluster_dir) # TODO :: Move arbitray path construction to StorageLayout Object backup_prefix = '{0}/basebackups_{1}/base_{file_name}_{file_offset}'\ .format(self.layout.prefix.rstrip('/'), FILE_STRUCTURE_VERSION, **start_backup_info) if rate_limit is None: per_process_limit = None else: per_process_limit = int(rate_limit / pool_size) # Reject tiny per-process rate limits. They should be # rejected more nicely elsewhere. assert per_process_limit is None or per_process_limit > 0 total_size = 0 # Make an attempt to upload extended version metadata extended_version_url = backup_prefix + '/extended_version.txt' logger.info(msg='start upload postgres version metadata', detail=('Uploading to {extended_version_url}.'.format( extended_version_url=extended_version_url))) uri_put_file(self.creds, extended_version_url, BytesIO(version.encode("utf8")), content_type='text/plain') logger.info(msg='postgres version metadata upload complete') uploader = PartitionUploader(self.creds, backup_prefix, per_process_limit, self.gpg_key_id) pool = TarUploadPool(uploader, pool_size) # Enqueue uploads for parallel execution for tpart in parts: total_size += tpart.total_member_size # 'put' can raise an exception for a just-failed upload, # aborting the process. pool.put(tpart) # Wait for remaining parts to upload. An exception can be # raised to signal failure of the upload. pool.join() return spec, backup_prefix, total_size