Allow rsync to use compression
From rsync's man page: -z, --compress With this option, rsync compresses the file data as it is sent to the destination machine, which reduces the amount of data being transmitted -- something that is useful over a slow connection. A configurable option has been added to allow rsync to compress, but only if the remote node is in a different region than the local one. NOTE: Objects that are already compressed (for example: .tar.gz, .mp3) might slow down the syncing process. On wire compression can also be extended to ssync later in a different change if required. In case of ssync, we could explore faster compression libraries like lz4. rsync uses zlib which is slow but offers higher compression ratio. Change-Id: Ic9b9cbff9b5e68bef8257b522cc352fc3544db3c Signed-off-by: Prashanth Pai <ppai@redhat.com>
This commit is contained in:
parent
41058ba809
commit
9c33bbde69
@ -114,6 +114,11 @@ use = egg:swift#recon
|
|||||||
# of run_pause.
|
# of run_pause.
|
||||||
# run_pause = 30
|
# run_pause = 30
|
||||||
#
|
#
|
||||||
|
# Allow rsync to compress data which is transmitted to destination node
|
||||||
|
# during sync. However, this is applicable only when destination node is in
|
||||||
|
# a different region than the local one.
|
||||||
|
# rsync_compress = no
|
||||||
|
#
|
||||||
# recon_cache_path = /var/cache/swift
|
# recon_cache_path = /var/cache/swift
|
||||||
|
|
||||||
[account-auditor]
|
[account-auditor]
|
||||||
|
@ -115,6 +115,11 @@ use = egg:swift#recon
|
|||||||
# of run_pause.
|
# of run_pause.
|
||||||
# run_pause = 30
|
# run_pause = 30
|
||||||
#
|
#
|
||||||
|
# Allow rsync to compress data which is transmitted to destination node
|
||||||
|
# during sync. However, this is applicable only when destination node is in
|
||||||
|
# a different region than the local one.
|
||||||
|
# rsync_compress = no
|
||||||
|
#
|
||||||
# recon_cache_path = /var/cache/swift
|
# recon_cache_path = /var/cache/swift
|
||||||
|
|
||||||
[container-updater]
|
[container-updater]
|
||||||
|
@ -174,6 +174,13 @@ use = egg:swift#recon
|
|||||||
# passed to rsync for io op timeout
|
# passed to rsync for io op timeout
|
||||||
# rsync_io_timeout = 30
|
# rsync_io_timeout = 30
|
||||||
#
|
#
|
||||||
|
# Allow rsync to compress data which is transmitted to destination node
|
||||||
|
# during sync. However, this is applicable only when destination node is in
|
||||||
|
# a different region than the local one.
|
||||||
|
# NOTE: Objects that are already compressed (for example: .tar.gz, .mp3) might
|
||||||
|
# slow down the syncing process.
|
||||||
|
# rsync_compress = no
|
||||||
|
#
|
||||||
# node_timeout = <whatever's in the DEFAULT section or 10>
|
# node_timeout = <whatever's in the DEFAULT section or 10>
|
||||||
# max duration of an http request; this is for REPLICATE finalization calls and
|
# max duration of an http request; this is for REPLICATE finalization calls and
|
||||||
# so should be longer than node_timeout
|
# so should be longer than node_timeout
|
||||||
|
@ -167,6 +167,8 @@ class Replicator(Daemon):
|
|||||||
self.vm_test_mode = config_true_value(conf.get('vm_test_mode', 'no'))
|
self.vm_test_mode = config_true_value(conf.get('vm_test_mode', 'no'))
|
||||||
self.node_timeout = int(conf.get('node_timeout', 10))
|
self.node_timeout = int(conf.get('node_timeout', 10))
|
||||||
self.conn_timeout = float(conf.get('conn_timeout', 0.5))
|
self.conn_timeout = float(conf.get('conn_timeout', 0.5))
|
||||||
|
self.rsync_compress = config_true_value(
|
||||||
|
conf.get('rsync_compress', 'no'))
|
||||||
self.reclaim_age = float(conf.get('reclaim_age', 86400 * 7))
|
self.reclaim_age = float(conf.get('reclaim_age', 86400 * 7))
|
||||||
swift.common.db.DB_PREALLOCATION = \
|
swift.common.db.DB_PREALLOCATION = \
|
||||||
config_true_value(conf.get('db_preallocation', 'f'))
|
config_true_value(conf.get('db_preallocation', 'f'))
|
||||||
@ -209,13 +211,16 @@ class Replicator(Daemon):
|
|||||||
('no_change', 'hashmatch', 'rsync', 'diff', 'ts_repl',
|
('no_change', 'hashmatch', 'rsync', 'diff', 'ts_repl',
|
||||||
'empty', 'diff_capped')]))
|
'empty', 'diff_capped')]))
|
||||||
|
|
||||||
def _rsync_file(self, db_file, remote_file, whole_file=True):
|
def _rsync_file(self, db_file, remote_file, whole_file=True,
|
||||||
|
different_region=False):
|
||||||
"""
|
"""
|
||||||
Sync a single file using rsync. Used by _rsync_db to handle syncing.
|
Sync a single file using rsync. Used by _rsync_db to handle syncing.
|
||||||
|
|
||||||
:param db_file: file to be synced
|
:param db_file: file to be synced
|
||||||
:param remote_file: remote location to sync the DB file to
|
:param remote_file: remote location to sync the DB file to
|
||||||
:param whole-file: if True, uses rsync's --whole-file flag
|
:param whole-file: if True, uses rsync's --whole-file flag
|
||||||
|
:param different_region: if True, the destination node is in a
|
||||||
|
different region
|
||||||
|
|
||||||
:returns: True if the sync was successful, False otherwise
|
:returns: True if the sync was successful, False otherwise
|
||||||
"""
|
"""
|
||||||
@ -224,6 +229,12 @@ class Replicator(Daemon):
|
|||||||
'--contimeout=%s' % int(math.ceil(self.conn_timeout))]
|
'--contimeout=%s' % int(math.ceil(self.conn_timeout))]
|
||||||
if whole_file:
|
if whole_file:
|
||||||
popen_args.append('--whole-file')
|
popen_args.append('--whole-file')
|
||||||
|
|
||||||
|
if self.rsync_compress and different_region:
|
||||||
|
# Allow for compression, but only if the remote node is in
|
||||||
|
# a different region than the local one.
|
||||||
|
popen_args.append('--compress')
|
||||||
|
|
||||||
popen_args.extend([db_file, remote_file])
|
popen_args.extend([db_file, remote_file])
|
||||||
proc = subprocess.Popen(popen_args)
|
proc = subprocess.Popen(popen_args)
|
||||||
proc.communicate()
|
proc.communicate()
|
||||||
@ -233,7 +244,8 @@ class Replicator(Daemon):
|
|||||||
return proc.returncode == 0
|
return proc.returncode == 0
|
||||||
|
|
||||||
def _rsync_db(self, broker, device, http, local_id,
|
def _rsync_db(self, broker, device, http, local_id,
|
||||||
replicate_method='complete_rsync', replicate_timeout=None):
|
replicate_method='complete_rsync', replicate_timeout=None,
|
||||||
|
different_region=False):
|
||||||
"""
|
"""
|
||||||
Sync a whole db using rsync.
|
Sync a whole db using rsync.
|
||||||
|
|
||||||
@ -243,6 +255,8 @@ class Replicator(Daemon):
|
|||||||
:param local_id: unique ID of the local database replica
|
:param local_id: unique ID of the local database replica
|
||||||
:param replicate_method: remote operation to perform after rsync
|
:param replicate_method: remote operation to perform after rsync
|
||||||
:param replicate_timeout: timeout to wait in seconds
|
:param replicate_timeout: timeout to wait in seconds
|
||||||
|
:param different_region: if True, the destination node is in a
|
||||||
|
different region
|
||||||
"""
|
"""
|
||||||
device_ip = rsync_ip(device['replication_ip'])
|
device_ip = rsync_ip(device['replication_ip'])
|
||||||
if self.vm_test_mode:
|
if self.vm_test_mode:
|
||||||
@ -253,14 +267,17 @@ class Replicator(Daemon):
|
|||||||
remote_file = '%s::%s/%s/tmp/%s' % (
|
remote_file = '%s::%s/%s/tmp/%s' % (
|
||||||
device_ip, self.server_type, device['device'], local_id)
|
device_ip, self.server_type, device['device'], local_id)
|
||||||
mtime = os.path.getmtime(broker.db_file)
|
mtime = os.path.getmtime(broker.db_file)
|
||||||
if not self._rsync_file(broker.db_file, remote_file):
|
if not self._rsync_file(broker.db_file, remote_file,
|
||||||
|
different_region=different_region):
|
||||||
return False
|
return False
|
||||||
# perform block-level sync if the db was modified during the first sync
|
# perform block-level sync if the db was modified during the first sync
|
||||||
if os.path.exists(broker.db_file + '-journal') or \
|
if os.path.exists(broker.db_file + '-journal') or \
|
||||||
os.path.getmtime(broker.db_file) > mtime:
|
os.path.getmtime(broker.db_file) > mtime:
|
||||||
# grab a lock so nobody else can modify it
|
# grab a lock so nobody else can modify it
|
||||||
with broker.lock():
|
with broker.lock():
|
||||||
if not self._rsync_file(broker.db_file, remote_file, False):
|
if not self._rsync_file(broker.db_file, remote_file,
|
||||||
|
whole_file=False,
|
||||||
|
different_region=different_region):
|
||||||
return False
|
return False
|
||||||
with Timeout(replicate_timeout or self.node_timeout):
|
with Timeout(replicate_timeout or self.node_timeout):
|
||||||
response = http.replicate(replicate_method, local_id)
|
response = http.replicate(replicate_method, local_id)
|
||||||
@ -363,7 +380,8 @@ class Replicator(Daemon):
|
|||||||
'put_timestamp', 'delete_timestamp', 'metadata')
|
'put_timestamp', 'delete_timestamp', 'metadata')
|
||||||
return tuple(info[key] for key in sync_args_order)
|
return tuple(info[key] for key in sync_args_order)
|
||||||
|
|
||||||
def _repl_to_node(self, node, broker, partition, info):
|
def _repl_to_node(self, node, broker, partition, info,
|
||||||
|
different_region=False):
|
||||||
"""
|
"""
|
||||||
Replicate a database to a node.
|
Replicate a database to a node.
|
||||||
|
|
||||||
@ -373,6 +391,8 @@ class Replicator(Daemon):
|
|||||||
:param info: DB info as a dictionary of {'max_row', 'hash', 'id',
|
:param info: DB info as a dictionary of {'max_row', 'hash', 'id',
|
||||||
'created_at', 'put_timestamp', 'delete_timestamp',
|
'created_at', 'put_timestamp', 'delete_timestamp',
|
||||||
'metadata'}
|
'metadata'}
|
||||||
|
:param different_region: if True, the destination node is in a
|
||||||
|
different region
|
||||||
|
|
||||||
:returns: True if successful, False otherwise
|
:returns: True if successful, False otherwise
|
||||||
"""
|
"""
|
||||||
@ -382,13 +402,16 @@ class Replicator(Daemon):
|
|||||||
response = http.replicate('sync', *sync_args)
|
response = http.replicate('sync', *sync_args)
|
||||||
if not response:
|
if not response:
|
||||||
return False
|
return False
|
||||||
return self._handle_sync_response(node, response, info, broker, http)
|
return self._handle_sync_response(node, response, info, broker, http,
|
||||||
|
different_region=different_region)
|
||||||
|
|
||||||
def _handle_sync_response(self, node, response, info, broker, http):
|
def _handle_sync_response(self, node, response, info, broker, http,
|
||||||
|
different_region=False):
|
||||||
if response.status == HTTP_NOT_FOUND: # completely missing, rsync
|
if response.status == HTTP_NOT_FOUND: # completely missing, rsync
|
||||||
self.stats['rsync'] += 1
|
self.stats['rsync'] += 1
|
||||||
self.logger.increment('rsyncs')
|
self.logger.increment('rsyncs')
|
||||||
return self._rsync_db(broker, node, http, info['id'])
|
return self._rsync_db(broker, node, http, info['id'],
|
||||||
|
different_region=different_region)
|
||||||
elif response.status == HTTP_INSUFFICIENT_STORAGE:
|
elif response.status == HTTP_INSUFFICIENT_STORAGE:
|
||||||
raise DriveNotMounted()
|
raise DriveNotMounted()
|
||||||
elif response.status >= 200 and response.status < 300:
|
elif response.status >= 200 and response.status < 300:
|
||||||
@ -403,7 +426,8 @@ class Replicator(Daemon):
|
|||||||
self.logger.increment('remote_merges')
|
self.logger.increment('remote_merges')
|
||||||
return self._rsync_db(broker, node, http, info['id'],
|
return self._rsync_db(broker, node, http, info['id'],
|
||||||
replicate_method='rsync_then_merge',
|
replicate_method='rsync_then_merge',
|
||||||
replicate_timeout=(info['count'] / 2000))
|
replicate_timeout=(info['count'] / 2000),
|
||||||
|
different_region=different_region)
|
||||||
# else send diffs over to the remote server
|
# else send diffs over to the remote server
|
||||||
return self._usync_db(max(rinfo['point'], local_sync),
|
return self._usync_db(max(rinfo['point'], local_sync),
|
||||||
broker, http, rinfo['id'], info['id'])
|
broker, http, rinfo['id'], info['id'])
|
||||||
@ -470,6 +494,11 @@ class Replicator(Daemon):
|
|||||||
return
|
return
|
||||||
responses = []
|
responses = []
|
||||||
nodes = self.ring.get_part_nodes(int(partition))
|
nodes = self.ring.get_part_nodes(int(partition))
|
||||||
|
local_dev = None
|
||||||
|
for node in nodes:
|
||||||
|
if node['id'] == node_id:
|
||||||
|
local_dev = node
|
||||||
|
break
|
||||||
if shouldbehere:
|
if shouldbehere:
|
||||||
shouldbehere = bool([n for n in nodes if n['id'] == node_id])
|
shouldbehere = bool([n for n in nodes if n['id'] == node_id])
|
||||||
# See Footnote [1] for an explanation of the repl_nodes assignment.
|
# See Footnote [1] for an explanation of the repl_nodes assignment.
|
||||||
@ -478,10 +507,23 @@ class Replicator(Daemon):
|
|||||||
i += 1
|
i += 1
|
||||||
repl_nodes = nodes[i + 1:] + nodes[:i]
|
repl_nodes = nodes[i + 1:] + nodes[:i]
|
||||||
more_nodes = self.ring.get_more_nodes(int(partition))
|
more_nodes = self.ring.get_more_nodes(int(partition))
|
||||||
|
if not local_dev:
|
||||||
|
# Check further if local device is a handoff node
|
||||||
|
for node in more_nodes:
|
||||||
|
if node['id'] == node_id:
|
||||||
|
local_dev = node
|
||||||
|
break
|
||||||
for node in repl_nodes:
|
for node in repl_nodes:
|
||||||
|
different_region = False
|
||||||
|
if local_dev and local_dev['region'] != node['region']:
|
||||||
|
# This additional information will help later if we
|
||||||
|
# want to handle syncing to a node in different
|
||||||
|
# region with some optimizations.
|
||||||
|
different_region = True
|
||||||
success = False
|
success = False
|
||||||
try:
|
try:
|
||||||
success = self._repl_to_node(node, broker, partition, info)
|
success = self._repl_to_node(node, broker, partition, info,
|
||||||
|
different_region)
|
||||||
except DriveNotMounted:
|
except DriveNotMounted:
|
||||||
repl_nodes.append(more_nodes.next())
|
repl_nodes.append(more_nodes.next())
|
||||||
self.logger.error(_('ERROR Remote drive not mounted %s'), node)
|
self.logger.error(_('ERROR Remote drive not mounted %s'), node)
|
||||||
|
@ -59,7 +59,8 @@ class ContainerReplicator(db_replicator.Replicator):
|
|||||||
'storage_policy_index'))
|
'storage_policy_index'))
|
||||||
return sync_args
|
return sync_args
|
||||||
|
|
||||||
def _handle_sync_response(self, node, response, info, broker, http):
|
def _handle_sync_response(self, node, response, info, broker, http,
|
||||||
|
different_region):
|
||||||
parent = super(ContainerReplicator, self)
|
parent = super(ContainerReplicator, self)
|
||||||
if is_success(response.status):
|
if is_success(response.status):
|
||||||
remote_info = json.loads(response.data)
|
remote_info = json.loads(response.data)
|
||||||
@ -74,7 +75,7 @@ class ContainerReplicator(db_replicator.Replicator):
|
|||||||
broker.merge_timestamps(*(remote_info[key] for key in
|
broker.merge_timestamps(*(remote_info[key] for key in
|
||||||
sync_timestamps))
|
sync_timestamps))
|
||||||
rv = parent._handle_sync_response(
|
rv = parent._handle_sync_response(
|
||||||
node, response, info, broker, http)
|
node, response, info, broker, http, different_region)
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def find_local_handoff_for_part(self, part):
|
def find_local_handoff_for_part(self, part):
|
||||||
|
@ -76,6 +76,8 @@ class ObjectReplicator(Daemon):
|
|||||||
self.rsync_timeout = int(conf.get('rsync_timeout', 900))
|
self.rsync_timeout = int(conf.get('rsync_timeout', 900))
|
||||||
self.rsync_io_timeout = conf.get('rsync_io_timeout', '30')
|
self.rsync_io_timeout = conf.get('rsync_io_timeout', '30')
|
||||||
self.rsync_bwlimit = conf.get('rsync_bwlimit', '0')
|
self.rsync_bwlimit = conf.get('rsync_bwlimit', '0')
|
||||||
|
self.rsync_compress = config_true_value(
|
||||||
|
conf.get('rsync_compress', 'no'))
|
||||||
self.http_timeout = int(conf.get('http_timeout', 60))
|
self.http_timeout = int(conf.get('http_timeout', 60))
|
||||||
self.lockup_timeout = int(conf.get('lockup_timeout', 1800))
|
self.lockup_timeout = int(conf.get('lockup_timeout', 1800))
|
||||||
self.recon_cache_path = conf.get('recon_cache_path',
|
self.recon_cache_path = conf.get('recon_cache_path',
|
||||||
@ -183,6 +185,11 @@ class ObjectReplicator(Daemon):
|
|||||||
'--contimeout=%s' % self.rsync_io_timeout,
|
'--contimeout=%s' % self.rsync_io_timeout,
|
||||||
'--bwlimit=%s' % self.rsync_bwlimit,
|
'--bwlimit=%s' % self.rsync_bwlimit,
|
||||||
]
|
]
|
||||||
|
if self.rsync_compress and \
|
||||||
|
job['region'] != node['region']:
|
||||||
|
# Allow for compression, but only if the remote node is in
|
||||||
|
# a different region than the local one.
|
||||||
|
args.append('--compress')
|
||||||
node_ip = rsync_ip(node['replication_ip'])
|
node_ip = rsync_ip(node['replication_ip'])
|
||||||
if self.vm_test_mode:
|
if self.vm_test_mode:
|
||||||
rsync_module = '%s::object%s' % (node_ip, node['replication_port'])
|
rsync_module = '%s::object%s' % (node_ip, node['replication_port'])
|
||||||
|
@ -92,22 +92,23 @@ class FakeRingWithNodes(object):
|
|||||||
class Ring(object):
|
class Ring(object):
|
||||||
devs = [dict(
|
devs = [dict(
|
||||||
id=1, weight=10.0, zone=1, ip='1.1.1.1', port=6000, device='sdb',
|
id=1, weight=10.0, zone=1, ip='1.1.1.1', port=6000, device='sdb',
|
||||||
meta=''
|
meta='', replication_ip='1.1.1.1', replication_port=6000, region=1
|
||||||
), dict(
|
), dict(
|
||||||
id=2, weight=10.0, zone=2, ip='1.1.1.2', port=6000, device='sdb',
|
id=2, weight=10.0, zone=2, ip='1.1.1.2', port=6000, device='sdb',
|
||||||
meta=''
|
meta='', replication_ip='1.1.1.2', replication_port=6000, region=2
|
||||||
), dict(
|
), dict(
|
||||||
id=3, weight=10.0, zone=3, ip='1.1.1.3', port=6000, device='sdb',
|
id=3, weight=10.0, zone=3, ip='1.1.1.3', port=6000, device='sdb',
|
||||||
meta=''
|
meta='', replication_ip='1.1.1.3', replication_port=6000, region=1
|
||||||
), dict(
|
), dict(
|
||||||
id=4, weight=10.0, zone=4, ip='1.1.1.4', port=6000, device='sdb',
|
id=4, weight=10.0, zone=4, ip='1.1.1.4', port=6000, device='sdb',
|
||||||
meta=''
|
meta='', replication_ip='1.1.1.4', replication_port=6000, region=2
|
||||||
), dict(
|
), dict(
|
||||||
id=5, weight=10.0, zone=5, ip='1.1.1.5', port=6000, device='sdb',
|
id=5, weight=10.0, zone=5, ip='1.1.1.5', port=6000, device='sdb',
|
||||||
meta=''
|
meta='', replication_ip='1.1.1.5', replication_port=6000, region=1
|
||||||
), dict(
|
), dict(
|
||||||
id=6, weight=10.0, zone=6, ip='1.1.1.6', port=6000, device='sdb',
|
id=6, weight=10.0, zone=6, ip='1.1.1.6', port=6000, device='sdb',
|
||||||
meta='')]
|
meta='', replication_ip='1.1.1.6', replication_port=6000, region=2
|
||||||
|
)]
|
||||||
|
|
||||||
def __init__(self, path, reload_time=15, ring_name=None):
|
def __init__(self, path, reload_time=15, ring_name=None):
|
||||||
pass
|
pass
|
||||||
@ -334,9 +335,26 @@ class TestDBReplicator(unittest.TestCase):
|
|||||||
'/some/file', 'remote:/some_file'],)
|
'/some/file', 'remote:/some_file'],)
|
||||||
self.assertEqual(exp_args, process.args)
|
self.assertEqual(exp_args, process.args)
|
||||||
|
|
||||||
|
def test_rsync_file_popen_args_different_region_and_rsync_compress(self):
|
||||||
|
replicator = TestReplicator({})
|
||||||
|
for rsync_compress in (False, True):
|
||||||
|
replicator.rsync_compress = rsync_compress
|
||||||
|
for different_region in (False, True):
|
||||||
|
with _mock_process(0) as process:
|
||||||
|
replicator._rsync_file('/some/file', 'remote:/some_file',
|
||||||
|
False, different_region)
|
||||||
|
if rsync_compress and different_region:
|
||||||
|
# --compress arg should be passed to rsync binary
|
||||||
|
# only when rsync_compress option is enabled
|
||||||
|
# AND destination node is in a different
|
||||||
|
# region
|
||||||
|
self.assertTrue('--compress' in process.args[0])
|
||||||
|
else:
|
||||||
|
self.assertFalse('--compress' in process.args[0])
|
||||||
|
|
||||||
def test_rsync_db(self):
|
def test_rsync_db(self):
|
||||||
replicator = TestReplicator({})
|
replicator = TestReplicator({})
|
||||||
replicator._rsync_file = lambda *args: True
|
replicator._rsync_file = lambda *args, **kwargs: True
|
||||||
fake_device = {'replication_ip': '127.0.0.1', 'device': 'sda1'}
|
fake_device = {'replication_ip': '127.0.0.1', 'device': 'sda1'}
|
||||||
replicator._rsync_db(FakeBroker(), fake_device, ReplHttp(), 'abcd')
|
replicator._rsync_db(FakeBroker(), fake_device, ReplHttp(), 'abcd')
|
||||||
|
|
||||||
@ -355,7 +373,8 @@ class TestDBReplicator(unittest.TestCase):
|
|||||||
self.db_file = db_file
|
self.db_file = db_file
|
||||||
self.remote_file = remote_file
|
self.remote_file = remote_file
|
||||||
|
|
||||||
def _rsync_file(self_, db_file, remote_file, whole_file=True):
|
def _rsync_file(self_, db_file, remote_file, whole_file=True,
|
||||||
|
different_region=False):
|
||||||
self.assertEqual(self_.db_file, db_file)
|
self.assertEqual(self_.db_file, db_file)
|
||||||
self.assertEqual(self_.remote_file, remote_file)
|
self.assertEqual(self_.remote_file, remote_file)
|
||||||
self_._rsync_file_called = True
|
self_._rsync_file_called = True
|
||||||
@ -403,7 +422,8 @@ class TestDBReplicator(unittest.TestCase):
|
|||||||
self.broker = broker
|
self.broker = broker
|
||||||
self._rsync_file_call_count = 0
|
self._rsync_file_call_count = 0
|
||||||
|
|
||||||
def _rsync_file(self_, db_file, remote_file, whole_file=True):
|
def _rsync_file(self_, db_file, remote_file, whole_file=True,
|
||||||
|
different_region=False):
|
||||||
self_._rsync_file_call_count += 1
|
self_._rsync_file_call_count += 1
|
||||||
if self_._rsync_file_call_count == 1:
|
if self_._rsync_file_call_count == 1:
|
||||||
self.assertEquals(True, whole_file)
|
self.assertEquals(True, whole_file)
|
||||||
@ -630,6 +650,20 @@ class TestDBReplicator(unittest.TestCase):
|
|||||||
[(('Found /path/to/file for /a%20c%20t/c%20o%20n when it should '
|
[(('Found /path/to/file for /a%20c%20t/c%20o%20n when it should '
|
||||||
'be on partition 0; will replicate out and remove.',), {})])
|
'be on partition 0; will replicate out and remove.',), {})])
|
||||||
|
|
||||||
|
def test_replicate_object_different_region(self):
|
||||||
|
db_replicator.ring = FakeRingWithNodes()
|
||||||
|
replicator = TestReplicator({})
|
||||||
|
replicator._repl_to_node = mock.Mock()
|
||||||
|
# For node_id = 1, one replica in same region(1) and other is in a
|
||||||
|
# different region(2). Refer: FakeRingWithNodes
|
||||||
|
replicator._replicate_object('0', '/path/to/file', 1)
|
||||||
|
# different_region was set True and passed to _repl_to_node()
|
||||||
|
self.assertEqual(replicator._repl_to_node.call_args_list[0][0][-1],
|
||||||
|
True)
|
||||||
|
# different_region was set False and passed to _repl_to_node()
|
||||||
|
self.assertEqual(replicator._repl_to_node.call_args_list[1][0][-1],
|
||||||
|
False)
|
||||||
|
|
||||||
def test_delete_db(self):
|
def test_delete_db(self):
|
||||||
db_replicator.lock_parent_directory = lock_parent_directory
|
db_replicator.lock_parent_directory = lock_parent_directory
|
||||||
replicator = TestReplicator({}, logger=unit.FakeLogger())
|
replicator = TestReplicator({}, logger=unit.FakeLogger())
|
||||||
@ -1202,7 +1236,8 @@ class TestReplToNode(unittest.TestCase):
|
|||||||
mock.call(self.broker, self.fake_node, self.http,
|
mock.call(self.broker, self.fake_node, self.http,
|
||||||
self.fake_info['id'],
|
self.fake_info['id'],
|
||||||
replicate_method='rsync_then_merge',
|
replicate_method='rsync_then_merge',
|
||||||
replicate_timeout=(self.fake_info['count'] / 2000))
|
replicate_timeout=(self.fake_info['count'] / 2000),
|
||||||
|
different_region=False)
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_repl_to_node_already_in_sync(self):
|
def test_repl_to_node_already_in_sync(self):
|
||||||
@ -1217,13 +1252,13 @@ class TestReplToNode(unittest.TestCase):
|
|||||||
def test_repl_to_node_not_found(self):
|
def test_repl_to_node_not_found(self):
|
||||||
self.http = ReplHttp('{"id": 3, "point": -1}', set_status=404)
|
self.http = ReplHttp('{"id": 3, "point": -1}', set_status=404)
|
||||||
self.assertEquals(self.replicator._repl_to_node(
|
self.assertEquals(self.replicator._repl_to_node(
|
||||||
self.fake_node, self.broker, '0', self.fake_info), True)
|
self.fake_node, self.broker, '0', self.fake_info, False), True)
|
||||||
self.replicator.logger.increment.assert_has_calls([
|
self.replicator.logger.increment.assert_has_calls([
|
||||||
mock.call.increment('rsyncs')
|
mock.call.increment('rsyncs')
|
||||||
])
|
])
|
||||||
self.replicator._rsync_db.assert_has_calls([
|
self.replicator._rsync_db.assert_has_calls([
|
||||||
mock.call(self.broker, self.fake_node, self.http,
|
mock.call(self.broker, self.fake_node, self.http,
|
||||||
self.fake_info['id'])
|
self.fake_info['id'], different_region=False)
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_repl_to_node_drive_not_mounted(self):
|
def test_repl_to_node_drive_not_mounted(self):
|
||||||
|
@ -1116,6 +1116,43 @@ class TestObjectReplicator(unittest.TestCase):
|
|||||||
'/a83', headers=self.headers))
|
'/a83', headers=self.headers))
|
||||||
mock_http.assert_has_calls(reqs, any_order=True)
|
mock_http.assert_has_calls(reqs, any_order=True)
|
||||||
|
|
||||||
|
def test_rsync_compress_different_region(self):
|
||||||
|
self.assertEqual(self.replicator.sync_method, self.replicator.rsync)
|
||||||
|
jobs = self.replicator.collect_jobs()
|
||||||
|
_m_rsync = mock.Mock(return_value=0)
|
||||||
|
_m_os_path_exists = mock.Mock(return_value=True)
|
||||||
|
with mock.patch.object(self.replicator, '_rsync', _m_rsync):
|
||||||
|
with mock.patch('os.path.exists', _m_os_path_exists):
|
||||||
|
for job in jobs:
|
||||||
|
self.assertTrue('region' in job)
|
||||||
|
for node in job['nodes']:
|
||||||
|
for rsync_compress in (True, False):
|
||||||
|
self.replicator.rsync_compress = rsync_compress
|
||||||
|
ret = \
|
||||||
|
self.replicator.sync(node, job,
|
||||||
|
['fake_suffix'])
|
||||||
|
self.assertTrue(ret)
|
||||||
|
if node['region'] != job['region']:
|
||||||
|
if rsync_compress:
|
||||||
|
# --compress arg should be passed to rsync
|
||||||
|
# binary only when rsync_compress option is
|
||||||
|
# enabled AND destination node is in a
|
||||||
|
# different region
|
||||||
|
self.assertTrue('--compress' in
|
||||||
|
_m_rsync.call_args[0][0])
|
||||||
|
else:
|
||||||
|
self.assertFalse('--compress' in
|
||||||
|
_m_rsync.call_args[0][0])
|
||||||
|
else:
|
||||||
|
self.assertFalse('--compress' in
|
||||||
|
_m_rsync.call_args[0][0])
|
||||||
|
self.assertEqual(
|
||||||
|
_m_os_path_exists.call_args_list[-1][0][0],
|
||||||
|
os.path.join(job['path'], 'fake_suffix'))
|
||||||
|
self.assertEqual(
|
||||||
|
_m_os_path_exists.call_args_list[-2][0][0],
|
||||||
|
os.path.join(job['path']))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user