如何测试该资源(基于文件的缓存以便在Perl中缓存webapp的输出)在并发访问所述共享资源的情况下表现得非常明智?
我写了一个简单的基于文件的缓存,用Perl编写,它使用锁定来序列化写访问,即只有一个(重新)生成缓存条目的进程。如果重要的话,这个缓存将用于缓存Perl webapp(gitweb)的输出。
我想测试一下,所说的缓存在并发访问下表现得很好,例如只有一个进程会运行用于生成缓存的子例程($cache->compute($key, sub { ... })
),所有进程都会获得生成的数据,如果进程写入缓存条目时,它不会使等待缓存(重新)生成等的进程死锁。
我该怎么办?我可以使用现成的Perl模块吗?
答案 0 :(得分:1)
最后,我的工作基于Aaron Crane的Unix for Perl programmers: pipes and processes;虽然在那些笔记中,他简化了一些事情,以便在没有锁定的情况下处理从多个进程中读取的内容(在这些说明中,临时文件用于第二个流)。
代码仅使用Test::More而不使用非核心Perl模块
#!/usr/bin/perl use warnings; use strict; use POSIX qw(dup2); use Fcntl qw(:DEFAULT); use IO::Handle; use IO::Select; use IO::Pipe; use Test::More; # [...] # from http://aaroncrane.co.uk/talks/pipes_and_processes/ sub fork_child (&) { my ($child_process_code) = @_; my $pid = fork(); die "Failed to fork: $!\n" if !defined $pid; return $pid if $pid != 0; # Now we're in the new child process $child_process_code->(); exit; } sub parallel_run (&) { my $child_code = shift; my $nchildren = 2; my %children; my (%pid_for_child, %fd_for_child); my $sel = IO::Select->new(); foreach my $child_idx (1..$nchildren) { my $pipe = IO::Pipe->new() or die "Failed to create pipe: $!\n"; my $pid = fork_child { $pipe->writer() or die "$$: Child \$pipe->writer(): $!\n"; dup2(fileno($pipe), fileno(STDOUT)) or die "$$: Child $child_idx failed to reopen stdout to pipe: $!\n"; close $pipe or die "$$: Child $child_idx failed to close pipe: $!\n"; # From Test-Simple-0.96/t/subtest/fork.t # # Force all T::B output into the pipe (redirected to STDOUT), # for the parent builder as well as the current subtest builder. { no warnings 'redefine'; *Test::Builder::output = sub { *STDOUT }; *Test::Builder::failure_output = sub { *STDOUT }; *Test::Builder::todo_output = sub { *STDOUT }; } $child_code->(); *STDOUT->flush(); close(STDOUT); }; $pid_for_child{$pid} = $child_idx; $pipe->reader() or die "Failed to \$pipe->reader(): $!\n"; $fd_for_child{$pipe} = $child_idx; $sel->add($pipe); $children{$child_idx} = { 'pid' => $pid, 'stdout' => $pipe, 'output' => '', }; } while (my @ready = $sel->can_read()) { foreach my $fh (@ready) { my $buf = ''; my $nread = sysread($fh, $buf, 1024); exists $fd_for_child{$fh} or die "Cannot find child for fd: $fh\n"; if ($nread > 0) { $children{$fd_for_child{$fh}}{'output'} .= $buf; } else { $sel->remove($fh); } } } while (%pid_for_child) { my $pid = waitpid -1, 0; warn "Child $pid_for_child{$pid} ($pid) failed with status: $?\n" if $? != 0; delete $pid_for_child{$pid}; } return map { $children{$_}{'output'} } keys %children; } # [...] @output = parallel_run { my $data = $cache->compute($key, \&get_value_slow); print $data; }; is_deeply( \@output, [ ($value) x 2 ], 'valid data returned by both process' );
答案 1 :(得分:0)
有两个过程:
一个过程应该是另一个过程的两倍。
测试是否在过程死亡时清理。 die
代替锁定。或者如果这是一个非常黑的框,当你希望进程拥有锁时,启动一个调用exit
的线程。
但是,我不确定你是如何让整个过程从单个线程中休眠的。
答案 2 :(得分:0)
我会使用Test :: Class和Test :: Exception作为创建测试的基础结构。
...例如,只有一个进程 会运行用于生成的子程序 缓存($ cache-> compute($ key,sub {... }))
应该可能变成这样:
sub test_inter_process_mutex {
# spawn process to acquire a lock, capture the pid
# assert I except when trying to acquire the lock
# send HUP signal to process, process releases lock and dies
}
将生成所有进程 数据
这更难。我可能会尝试隔离通信机制并断言它以某种方式工作。
表示如果进程写入缓存条目 死了它不会死锁进程 等待(重新)生成缓存 等
变为:
sub test_no_process_deathgrip {
# spawn process to acquire the lock and then except
# assert I can acquire the lock
# for signals HUP, SIGINT, TERM, and KILL
# spawn a process to acquire the lock, capture pid
# send signal to process
# assert I can acquire the lock
}
}