'High-level method that replicates a single partition.
:param job: a dict containing info about the partition to be replicated'

def update(self, job):

    self.replication_count += 1
    self.logger.increment(('partition.update.count.%s' % (job['device'],)))
    headers = dict(self.default_headers)
    headers['X-Backend-Storage-Policy-Index'] = int(job['policy'])
    target_devs_info = set()
    failure_devs_info = set()
    begin = time.time()
    df_mgr = self._df_router[job['policy']]
    try:
        (hashed, local_hash) = tpool_reraise(df_mgr._get_hashes, job['device'], job['partition'], job['policy'], do_listdir=_do_listdir(int(job['partition']), self.replication_cycle))
        self.suffix_hash += hashed
        self.logger.update_stats('suffix.hashes', hashed)
        attempts_left = len(job['nodes'])
        synced_remote_regions = set()
        random.shuffle(job['nodes'])
        nodes = itertools.chain(job['nodes'], job['policy'].object_ring.get_more_nodes(int(job['partition'])))
        while (attempts_left > 0):
            node = next(nodes)
            target_devs_info.add((node['replication_ip'], node['device']))
            attempts_left -= 1
            if (node['region'] in synced_remote_regions):
                continue
            try:
                with Timeout(self.http_timeout):
                    resp = http_connect(node['replication_ip'], node['replication_port'], node['device'], job['partition'], 'REPLICATE', '', headers=headers).getresponse()
                    if (resp.status == HTTP_INSUFFICIENT_STORAGE):
                        self.logger.error(_('%(replication_ip)s/%(device)s    responded    as    unmounted'), node)
                        attempts_left += 1
                        failure_devs_info.add((node['replication_ip'], node['device']))
                        continue
                    if (resp.status != HTTP_OK):
                        self.logger.error(_('Invalid    response    %(resp)s    from    %(ip)s'), {'resp': resp.status, 'ip': node['replication_ip']})
                        failure_devs_info.add((node['replication_ip'], node['device']))
                        continue
                    remote_hash = pickle.loads(resp.read())
                    del resp
                suffixes = [suffix for suffix in local_hash if (local_hash[suffix] != remote_hash.get(suffix, (-1)))]
                if (not suffixes):
                    self.stats['hashmatch'] += 1
                    continue
                (hashed, recalc_hash) = tpool_reraise(df_mgr._get_hashes, job['device'], job['partition'], job['policy'], recalculate=suffixes)
                self.logger.update_stats('suffix.hashes', hashed)
                local_hash = recalc_hash
                suffixes = [suffix for suffix in local_hash if (local_hash[suffix] != remote_hash.get(suffix, (-1)))]
                self.stats['rsync'] += 1
                (success, _junk) = self.sync(node, job, suffixes)
                with Timeout(self.http_timeout):
                    conn = http_connect(node['replication_ip'], node['replication_port'], node['device'], job['partition'], 'REPLICATE', ('/' + '-'.join(suffixes)), headers=headers)
                    conn.getresponse().read()
                if (not success):
                    failure_devs_info.add((node['replication_ip'], node['device']))
                if (success and (node['region'] != job['region'])):
                    synced_remote_regions.add(node['region'])
                self.suffix_sync += len(suffixes)
                self.logger.update_stats('suffix.syncs', len(suffixes))
            except (Exception, Timeout):
                failure_devs_info.add((node['replication_ip'], node['device']))
                self.logger.exception((_('Error    syncing    with    node:    %s') % node))
        self.suffix_count += len(local_hash)
    except (Exception, Timeout):
        failure_devs_info.update(target_devs_info)
        self._add_failure_stats(failure_devs_info)
        self.logger.exception(_('Error    syncing    partition'))
    finally:
        self.stats['success'] += len((target_devs_info - failure_devs_info))
        self.partition_times.append((time.time() - begin))
        self.logger.timing_since('partition.update.timing', begin)
