Apache-ASP
view release on metacpan or search on metacpan
lib/Apache/ASP/StateManager.pm view on Meta::CPAN
if(my $count = $member_state->Delete()) {
$asp->{dbg} &&
$asp->Debug("deleting session", {
session_id => $id,
files_deleted => $count,
});
$deleted++;
delete $internal->{$id};
} else {
$asp->Error("can't delete session id: $id");
return; # no next in eval {}
}
};
if($@) {
$asp->Error("error for cleanup of session id $id: $@");
}
}
$internal->UNLOCK();
#### LEAVE DIRECTORIES, NASTY RACE CONDITION POTENTIAL
## NOW PRUNE ONLY DIRECTORIES THAT WE DON'T NEED TO KEEP
## FOR PERFORMANCE
# REMOVE DIRECTORY, LOCK
# if the directory is still empty, remove it, lock it
# down so no new sessions will be created in it while we
# are testing
if($deleted == @$ids) {
if ($state->GroupId !~ /^[0]/) {
$asp->{Internal}->LOCK();
my $ids = $state->GroupMembers();
if(@{$ids} == 0) {
$self->Log("purging stale group ".$state->GroupId.", which should only happen ".
"after Apache::ASP upgrade to beyond 2.09");
$state->DeleteGroupId();
}
$asp->{Internal}->UNLOCK();
}
}
$deleted;
}
sub CleanupGroups {
my($self, $force) = @_;
return unless $self->{Session};
my $cleanup = 0;
my $state_dir = $self->{state_dir};
my $internal = $self->{Internal};
$force ||= 0;
$self->Debug("forcing groups cleanup") if ($self->{dbg} && $force);
# each apache process has an internal time in which it
# did its last check, once we have passed that, we check
# $Internal for the last time the check was done. We
# break it up in this way so that locking on $Internal
# does not become another bottleneck for scripts
if($force || ($Apache::ASP::CleanupGroups{$state_dir} || 0) < time()) {
# /8 to keep it less bursty... since we check groups every group_refresh/2
# we'll average 1/4 of the groups everytime we check them on a busy server
$Apache::ASP::CleanupGroups{$state_dir} = time() + $self->{group_refresh}/8;
$self->{dbg} && $self->Debug("testing internal time for cleanup groups");
if($self->CleanupMaster) {
$internal->LOCK();
if($force || ($internal->{CleanupGroups} < (time - $self->{group_refresh}/8))) {
$internal->{CleanupGroups} = time;
$cleanup = 1;
}
$internal->UNLOCK;
}
}
return unless $cleanup;
# clean cache, so caching won't affect CleanupGroups() being called multiple times
$self->{internal_cached_keys} = undef;
# only one process doing CleanupGroup at a time now, so OK
# lock around, necessary when keeping empty group directories
my $groups = $self->{Session}{_SELF}{'state'}->DefaultGroups();
$self->{dbg} && $self->Debug("groups ", $groups);
my($sum_active, $sum_deleted);
$internal->LOCK();
my $start_cleanup = time;
for(@{$groups}) {
$sum_deleted = $self->CleanupGroup($_, $force);
if ($start_cleanup > time) {
# every second, take a breather in the lock management
# so that sessions can be created, and the like, so for
# long purges, the application will get sticky in 1 second
# bursts
$start_cleanup = time;
$internal->UNLOCK;
$internal->LOCK;
last unless $self->CleanupMaster;
}
}
$internal->UNLOCK();
$self->{dbg} && $self->Debug("cleanup groups", { deleted => $sum_deleted }) if $self->{dbg};
# boolean true at least for master
$sum_deleted || 1;
}
sub CleanupMaster {
my $self = shift;
my $internal = $self->{Internal};
$internal->LOCK;
my $master = $internal->{CleanupMaster} ||
{
ServerID => '',
PID => 0,
Checked => 0,
};
my $is_master = (($master->{ServerID} eq $ServerID) and ($master->{PID} eq $$)) ? 1 : 0;
$self->{dbg} && $self->Debug(current_master => $master, is_master => $is_master );
my $stale_time = $is_master ? $self->{group_refresh} / 4 :
$self->{group_refresh} / 2 + int($self->{group_refresh} * rand() / 2) + 1;
$stale_time += $master->{Checked};
( run in 1.010 second using v1.01-cache-2.11-cpan-39bf76dae61 )