aboutsummaryrefslogtreecommitdiffstats
path: root/research/experiments/019-idle-links/run.py
diff options
context:
space:
mode:
Diffstat (limited to 'research/experiments/019-idle-links/run.py')
-rw-r--r--research/experiments/019-idle-links/run.py57
1 files changed, 51 insertions, 6 deletions
diff --git a/research/experiments/019-idle-links/run.py b/research/experiments/019-idle-links/run.py
index 15ae2de..7f2647d 100644
--- a/research/experiments/019-idle-links/run.py
+++ b/research/experiments/019-idle-links/run.py
@@ -22,7 +22,11 @@ send it until it is stored; at --kill-at seconds the receiver is killed
both sides before and after, the X25519 made (none when the node
remembers its secrets; --no-memo forgets them), and that every order made
was applied once. With --quic too, the same for quinn clients, which
-connect again when their connection is lost.
+connect again when their connection is lost. With --move too (M3), in
+namespaces of its own (unshare -rmn), the devices send to receiver.test
+(127.0.0.2), and the node is started again elsewhere: its store, key and
+memo copied, 127.0.0.2 made unreachable, the name pointed at 127.0.0.3;
+the devices look the name up again when their probes go unanswered.
With --first-contact DELAYS (M4), for each one-way delay in ms, in a
network namespace of its own (unshare -rn) whose loopback delays UDP:
@@ -236,9 +240,10 @@ def recovery(timeline, killed_ms):
return {'steady_per_100ms': round(steady, 1), 'silent_100ms_steps': silent, 'caught_up_ms': None}
-def restart_pure(n, work, keys, module, period_ms, seconds, kill_at, memo):
+def restart_pure(n, work, keys, module, period_ms, seconds, kill_at, memo, host='127.0.0.1', move=None):
"""M2 for Pure: n devices send a record every period_ms; the node is
- killed (-9) at kill_at seconds and started again at once."""
+ killed (-9) at kill_at seconds and started again at once (M3, with move:
+ elsewhere, move(args) giving its arguments there)."""
senders = work / f'senders-{n}'
with open(f'{keys}.senders') as source, open(senders, 'w') as out:
for _, line in zip(range(n), source):
@@ -249,7 +254,7 @@ def restart_pure(n, work, keys, module, period_ms, seconds, kill_at, memo):
args = [ROOT / 'build/pure-serve', '--module', module, '--store', store, '--port', port, '--key', work / 'node.key',
'--link-port', link_port, '--senders', senders]
node, info = start(args)
- generator = subprocess.Popen([str(a) for a in [ROOT / 'build/links', 'run', keys, module, 'order', '127.0.0.1',
+ generator = subprocess.Popen([str(a) for a in [ROOT / 'build/links', 'run', keys, module, 'order', host,
link_port, info['public_key'], 0, n, period_ms, seconds]],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for line in generator.stderr: # progress, a line a second
@@ -267,8 +272,11 @@ def restart_pure(n, work, keys, module, period_ms, seconds, kill_at, memo):
killed_ms = int((t + killed - seen) * 1000) # on the generator's clock
if not memo:
(work / 'node.key.links').unlink()
+ if move:
+ args = move(args)
node, _ = start(args)
- row = {'devices': n, 'period_ms': period_ms, 'memo': memo, 'restart_ms': round((time.monotonic() - killed) * 1000, 1),
+ row = {'devices': n, 'period_ms': period_ms, 'memo': memo, 'moved': bool(move),
+ 'restart_ms': round((time.monotonic() - killed) * 1000, 1),
'steady_cpu_ms_per_s': round(steady_cpu, 1), 'senders_steady_cpu_ms_per_s': round(senders_steady, 1)}
cpu_start, senders_start = cpu_ms(node.pid), cpu_ms(generator.pid)
time.sleep(10)
@@ -290,6 +298,28 @@ def restart_pure(n, work, keys, module, period_ms, seconds, kill_at, memo):
return row
+def mover(hosts, ip):
+ """M3: the node's store, key and memo carried to another machine (copied,
+ in the namespace of --move), its old address (127.0.0.2) made
+ unreachable, and the name the devices send to (receiver.test) pointed at
+ the new one (127.0.0.3)."""
+ import shutil
+
+ def move(args):
+ args = list(args)
+ store, key = Path(args[args.index('--store') + 1]), Path(args[args.index('--key') + 1])
+ moved_store, moved_key = store.with_name(store.name + '-moved'), key.with_name('moved-' + key.name)
+ shutil.copytree(store, moved_store)
+ shutil.copy(key, moved_key)
+ if Path(f'{key}.links').exists():
+ shutil.copy(f'{key}.links', f'{moved_key}.links')
+ subprocess.run([ip, 'route', 'add', 'unreachable', '127.0.0.2/32', 'table', 'local'], check=True)
+ hosts.write_text('127.0.0.1 localhost\n127.0.0.3 receiver.test\n')
+ args[args.index('--store') + 1], args[args.index('--key') + 1] = moved_store, moved_key
+ return args
+ return move
+
+
def restart_quic(n, work, period_ms, seconds, kill_at):
"""M2 for quinn: the same load; the service is killed (-9) and started
again on its port, with the same key (so it resets the old connections)."""
@@ -539,6 +569,9 @@ def main():
parser.add_argument('--quic', action='store_true', help='measure the quinn service instead (quic/)')
parser.add_argument('--restart', action='store_true', help='M2: kill -9 the receiver under load (with --quic, quinn)')
parser.add_argument('--no-memo', action='store_true', help='M2: the node forgets the secrets it remembered')
+ parser.add_argument('--move', action='store_true',
+ help='M3 (with --restart): started again on another machine, the store carried there (a '
+ 'namespace of its own: unshare -rmn)')
parser.add_argument('--period-ms', type=int, default=5000)
parser.add_argument('--seconds', type=int, default=40)
parser.add_argument('--kill-at', type=int, default=20)
@@ -551,6 +584,17 @@ def main():
parser.add_argument('--rounds', type=int, default=1)
args = parser.parse_args()
counts = [int(c) for c in args.counts.split(',')]
+ if args.move and not os.environ.get('PURE_MOVE_HOSTS'): # in namespaces of its own: its network, its /etc/hosts
+ with tempfile.TemporaryDirectory(prefix='pure-m3-', dir=args.store) as directory:
+ hosts = Path(directory) / 'hosts'
+ hosts.write_text('127.0.0.1 localhost\n127.0.0.2 receiver.test\n')
+ done = subprocess.run(['unshare', '-rmn', sys.executable] + sys.argv, env={**os.environ, 'PURE_MOVE_HOSTS': str(hosts)})
+ sys.exit(done.returncode)
+ if args.move:
+ import shutil
+ ip = shutil.which('ip') or '/usr/sbin/ip'
+ subprocess.run([ip, 'link', 'set', 'lo', 'up'], check=True)
+ subprocess.run(['mount', '--bind', os.environ['PURE_MOVE_HOSTS'], '/etc/hosts'], check=True)
if args.transport:
subprocess.run(['make', '-s', '-C', str(ROOT), 'build/libpure.a', 'build/libpure-link.a', 'build/pure-crypto.so'],
check=True)
@@ -614,7 +658,8 @@ def main():
subprocess.run([str(ROOT / 'build/links'), 'keys', str(max(counts)), str(keys)], check=True)
for n in counts:
row = restart_quic(n, work, args.period_ms, args.seconds, args.kill_at) if args.quic else \
- restart_pure(n, work, keys, module, args.period_ms, args.seconds, args.kill_at, not args.no_memo)
+ restart_pure(n, work, keys, module, args.period_ms, args.seconds, args.kill_at, not args.no_memo,
+ *(('receiver.test', mover(Path(os.environ['PURE_MOVE_HOSTS']), ip)) if args.move else ()))
line = json.dumps(row)
print(line, flush=True)
if args.out: