tests: Test concurrent operations
Test that concurrent commits and prunes can succeed. Mostly this is a check that the new locking works correctly and the concurrent processes will properly wait until they've acquired the appropriate repository lock. Closes: #1343 Approved by: cgwalters
This commit is contained in:
parent
df7f33e498
commit
3f4506f088
|
|
@ -108,6 +108,7 @@ _installed_or_uninstalled_test_scripts = \
|
||||||
tests/test-xattrs.sh \
|
tests/test-xattrs.sh \
|
||||||
tests/test-auto-summary.sh \
|
tests/test-auto-summary.sh \
|
||||||
tests/test-prune.sh \
|
tests/test-prune.sh \
|
||||||
|
tests/test-concurrency.py \
|
||||||
tests/test-refs.sh \
|
tests/test-refs.sh \
|
||||||
tests/test-demo-buildsystem.sh \
|
tests/test-demo-buildsystem.sh \
|
||||||
tests/test-switchroot.sh \
|
tests/test-switchroot.sh \
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 Colin Walters <walters@verbum.org>
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the
|
||||||
|
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
# Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from multiprocessing import cpu_count
|
||||||
|
|
||||||
|
def fatal(msg):
|
||||||
|
sys.stderr.write(msg)
|
||||||
|
sys.stderr.write('\n')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Create 20 files with content based on @dname + a serial, basically to have
|
||||||
|
# different files with different checksums.
|
||||||
|
def mktree(dname, serial=0):
|
||||||
|
print('Creating tree', dname, file=sys.stderr)
|
||||||
|
os.mkdir(dname, 0755)
|
||||||
|
for v in xrange(20):
|
||||||
|
with open('{}/{}'.format(dname, v), 'w') as f:
|
||||||
|
f.write('{} {} {}\n'.format(dname, serial, v))
|
||||||
|
|
||||||
|
subprocess.check_call(['ostree', '--repo=repo', 'init', '--mode=bare'])
|
||||||
|
# like the bit in libtest, but let's do it unconditionally since it's simpler,
|
||||||
|
# and we don't need xattr coverage for this
|
||||||
|
with open('repo/config', 'a') as f:
|
||||||
|
f.write('disable-xattrs=true\n')
|
||||||
|
|
||||||
|
def commit(v):
|
||||||
|
tdir='tree{}'.format(v)
|
||||||
|
cmd = ['ostree', '--repo=repo', 'commit', '--fsync=0', '-b', tdir, '--tree=dir='+tdir]
|
||||||
|
proc = subprocess.Popen(cmd)
|
||||||
|
print('PID {}'.format(proc.pid), *cmd, file=sys.stderr)
|
||||||
|
return proc
|
||||||
|
def prune():
|
||||||
|
cmd = ['ostree', '--repo=repo', 'prune', '--refs-only']
|
||||||
|
proc = subprocess.Popen(cmd)
|
||||||
|
print('PID {}:'.format(proc.pid), *cmd, file=sys.stderr)
|
||||||
|
return proc
|
||||||
|
|
||||||
|
def wait_check(proc):
|
||||||
|
pid = proc.pid
|
||||||
|
proc.wait()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
sys.stderr.write("process {} exited with code {}\n".format(proc.pid, proc.returncode))
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
sys.stderr.write('PID {} exited OK\n'.format(pid))
|
||||||
|
return True
|
||||||
|
|
||||||
|
print("1..2")
|
||||||
|
|
||||||
|
def run(n_committers, n_pruners):
|
||||||
|
# The number of committers needs to be even since we only create half as
|
||||||
|
# many trees
|
||||||
|
n_committers += n_committers % 2
|
||||||
|
|
||||||
|
committers = set()
|
||||||
|
pruners = set()
|
||||||
|
|
||||||
|
print('n_committers', n_committers, 'n_pruners', n_pruners, file=sys.stderr)
|
||||||
|
n_trees = n_committers / 2
|
||||||
|
for v in xrange(n_trees):
|
||||||
|
mktree('tree{}'.format(v))
|
||||||
|
|
||||||
|
for v in xrange(n_committers):
|
||||||
|
committers.add(commit(v / 2))
|
||||||
|
for v in xrange(n_pruners):
|
||||||
|
pruners.add(prune())
|
||||||
|
|
||||||
|
failed = False
|
||||||
|
for committer in committers:
|
||||||
|
if not wait_check(committer):
|
||||||
|
failed = True
|
||||||
|
for pruner in pruners:
|
||||||
|
if not wait_check(pruner):
|
||||||
|
failed = True
|
||||||
|
if failed:
|
||||||
|
fatal('A child process exited abnormally')
|
||||||
|
|
||||||
|
for v in xrange(n_trees):
|
||||||
|
shutil.rmtree('tree{}'.format(v))
|
||||||
|
|
||||||
|
# No concurrent pruning
|
||||||
|
run(cpu_count()/2 + 2, 0)
|
||||||
|
print("ok no concurrent prunes")
|
||||||
|
|
||||||
|
run(cpu_count()/2 + 4, 3)
|
||||||
|
print("ok concurrent prunes")
|
||||||
Loading…
Reference in New Issue