ALT Linux Team development discussions
 help / color / mirror / Atom feed
From: Alexey Tourbin <at@altlinux.ru>
To: devel@lists.altlinux.org
Subject: Re: [devel] вопрос про BDB и блокировки.
Date: Fri, 27 Apr 2007 14:20:34 +0400
Message-ID: <20070427102034.GB11839@solemn.turbinal> (raw)
In-Reply-To: <200704271505.10158.asy@altlinux.ru>


[-- Attachment #1.1: Type: text/plain, Size: 1734 bytes --]

On Fri, Apr 27, 2007 at 03:05:09PM +0500, Sergey Y. Afonin wrote:
> Хочется, в данной ситации, "добить" проблему с mailfromd. В документации
> http://www.oracle.com/technology/documentation/berkeley-db/db/ref/lock/intro.html
> утверждается, что The Lock subsystem is created, initialized, and opened by 
> calls to DB_ENV->open with the DB_INIT_LOCK or DB_INIT_CDB flags specified.
> То есть, что встроенные блокировки в BDB начинают работать только в случае
> использования флагов DB_INIT_LOCK или DB_INIT_CDB. По некоторой причине (в 

DB_INIT_CDB дает "прозрачный" локинг для каждой операции. 
То есто операция начинается берётся блокировка.  Операция заканчивает
блокировка снимается.

DB_INIT_LOCK по идее специально указывать не надо, если указано
DB_INIT_CDB|DB_INIT_MPOOL.

Там есть ещё другой режим вместо локинга, он log может писать.

> эти подробноcти я не вдавался, утверждается, что не всё гладко) автор 
> mailfromd решил делать блокировки самостоятельно. Я, вроде как, единственный 
> из нарвавшихся на проблему с его блокировками. Используя метод научного тыка 
> и по совету человека, который кое-где bdb сам использовал, я просто убрал 
> блокировку (про то, что она активируется при использовании DB_INIT_LOCK или 
> DB_INIT_CDB, я ещё не дочитал на тот момент).

Вот кусок перлового кода.  Тут есть две тонкости: открывать env надо
через exclusive lock, иначе там глюкало.  Я это делаю через flock на
дескрипторе каталога (круто!).  И ещё одна тонкость что нужно сигналы
обязательно блокировать на время взятия блокировки.  Кажется, факт
блокировки фиксируется физически в самой базе.  Поэтому если по
пришествии сигнала блокировку не снять, то база останется залоченной
"надолго".

[-- Attachment #1.2: cache.pm --]
[-- Type: text/plain, Size: 4355 bytes --]

package qa::cache;

use strict;
use BerkeleyDB;

our $topdir = "$ENV{HOME}/.qa-cache";
my $topdir_fd;
my $dbenv;

sub init_dbenv () {
	use Fcntl qw(:flock O_DIRECTORY);
	-d $topdir or mkdir $topdir;
	sysopen $topdir_fd, $topdir, O_DIRECTORY or die "$topdir: $!";
	if (flock $topdir_fd, LOCK_EX | LOCK_NB) {
		$dbenv = BerkeleyDB::Env->new(-Home => $topdir,
			-Verbose => 1, -ErrFile => *STDERR,
			-Flags => DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL)
				or die $BerkeleyDB::Error;
		# TODO: drop all locks
		flock $topdir_fd, LOCK_SH;
	}
	else {
		flock $topdir_fd, LOCK_SH;
		$dbenv = BerkeleyDB::Env->new(-Home => $topdir,
			-Verbose => 1, -ErrFile => *STDERR,
			-Flags => DB_JOINENV)
				or die $BerkeleyDB::Error;
	}
}

my %blessed;
my $pagesize;

sub TIEHASH ($$) {
	my ($class, $id) = @_;
	return $blessed{$id} if $blessed{$id};
	init_dbenv() unless $dbenv;
	my $dir = "$topdir/$id";
	-d $dir or mkdir $dir;
	my $db = BerkeleyDB::Hash->new(-Filename => "$id/cache.db",
		-Env => $dbenv, -Flags => DB_CREATE)
			or die $BerkeleyDB::Error;
	$pagesize ||= $db->db_stat->{hash_pagesize};
	my $self = bless [ $dir, $db ] => $class;
	$blessed{$id} = $self;
	use Scalar::Util qw(weaken);
	weaken $blessed{$id};
	return $self;
}

use Storable qw(freeze thaw);
use Compress::LZO qw(compress decompress);
use Digest::SHA1 qw(sha1);

use constant {
	V_STO	=> 2**1,	# STO is Special Theory of Relativity
	V_LZO	=> 2**2,	# LZO is real-time compressor
};

my $today = int($^T / 3600 / 24);

sub STORE ($$$) {
	my ($self, $k, $v) = @_;
	$k = freeze($k) if ref $k;
	$k = sha1($k);
	my $vflags = 0;
	if (ref $v) {
		$v = freeze($v);
		$vflags |= V_STO;
	}
	if (length($v) > 768) {
		$v = compress($v);
		$vflags |= V_LZO;
	}
	my ($dir, $db) = @$self;
	if (length($v) > $pagesize / 2) {
		my ($subdir, $file) = unpack "H2H*", $k;
		$subdir = "$dir/$subdir";
		$file = "$subdir/$file";
		-d $subdir or mkdir $subdir;
		open my $fh, ">", "$file.$$" or die $!;
		syswrite $fh, pack("S", $vflags);
		syswrite $fh, $v;
		close $fh;
		rename "$file.$$", $file;
	}
	else {	# SSS: mtime, atime, vflags
		$db->db_put($k, pack("SSS", $today, 0, $vflags) . $v);
	}
}

sub FETCH ($$) {
	my ($self, $k) = @_;
	$k = freeze($k) if ref $k;
	$k = sha1($k);
	my ($dir, $db) = @$self;
	my ($vflags, $v);
	if ($db->db_get($k, $v) == 0) {
		(my $m, my $a, $vflags) = unpack "SSS", $v;
		substr $v, 0, 6, "";
		$db->db_put($k, pack("SSS", $m, $today, $vflags) . $v)
			if $a != $today; # XXX not atomic
	}
	else {
		my ($subdir, $file) = unpack "H2H*", $k;
		$subdir = "$dir/$subdir";
		$file = "$subdir/$file";
		open my $fh, "<", $file or return;
		local $/;
		$v = <$fh>;
		$vflags = unpack "S", $v;
		substr $v, 0, 2, "";
	}
	$v = decompress($v) if $vflags & V_LZO;
	$v = thaw($v) if $vflags & V_STO;
	return $v;
}

sub EXISTS ($$) {
	my ($self, $k) = @_;
	$k = freeze($k) if ref($k);
	$k = sha1($k);
	my ($dir, $db) = @$self;
	return 1 if $db->db_get($k, my $v) == 0;
	my ($subdir, $file) = unpack "H2H*", $k;
	$subdir = "$dir/$subdir";
	$file = "$subdir/$file";
	return -f $file;
}

sub DELETE ($$) {
	my ($self, $k) = @_;
	$k = freeze($k) if ref($k);
	$k = sha1($k);
	my ($dir, $db) = @$self;
	$db->db_del($k);
	my ($subdir, $file) = unpack "H2H*", $k;
	$subdir = "$dir/$subdir";
	$file = "$subdir/$file";
	unlink $file;
}

# BerkeleyDB cleans up at the END, so do I
my $global_destruction;

# execute the END when interrupted by a signal --
# it is VERY important to release all locks and shut down gracefully
use sigtrap qw(die normal-signals);

our $expire = 33;

sub DESTROY ($) {
	return if $global_destruction;
	my $self = shift;
	my ($dir, $db) = @$self;
	my $cur = $db->_db_write_cursor() or return;
	if ($db->db_get("cleanup", my $cleanup) != 0) {
		$db->db_put("cleanup", $today);
		return;
	}
	elsif ($cleanup == $today) {
		return;
	}
	while ($cur->c_get(my $k, my $v, DB_NEXT) == 0) {
		next if $k eq "cleanup";
		my ($m, $a, $vflags) = unpack "SSS", $v;
		next if $a + 33 > $today;
		next if $m + 33 > $today;
		$cur->c_del();
	}
	my $wanted = sub {
		stat or return;
		-f _ and -M _ > $expire and -A _ > $expire and unlink;
		-d _ and rmdir;
	};
	require File::Find;
	File::Find::finddepth($wanted, $dir);
}

END {
	undef $dbenv;
	while (my ($id, $self) = each %blessed) {
		next unless $self;
		$self->DESTROY();
		undef @$self;
	}
	$global_destruction = 1;
}

1;

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

  reply	other threads:[~2007-04-27 10:20 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-04-27 10:05 Sergey Y. Afonin
2007-04-27 10:20 ` Alexey Tourbin [this message]
2007-04-27 10:38   ` Sergey Y. Afonin
2007-04-27 11:02     ` Alexey Tourbin
2007-04-27 12:00       ` Sergey Y. Afonin
2007-04-27 12:07         ` Sergey Y. Afonin
2007-04-27 11:19     ` Alexey Tourbin
2007-04-27 11:40       ` Sergey Y. Afonin
2007-04-27 10:24 ` Sergey Y. Afonin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070427102034.GB11839@solemn.turbinal \
    --to=at@altlinux.ru \
    --cc=devel@lists.altlinux.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

ALT Linux Team development discussions

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://lore.altlinux.org/devel/0 devel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 devel devel/ http://lore.altlinux.org/devel \
		devel@altlinux.org devel@altlinux.ru devel@lists.altlinux.org devel@lists.altlinux.ru devel@linux.iplabs.ru mandrake-russian@linuxteam.iplabs.ru sisyphus@linuxteam.iplabs.ru
	public-inbox-index devel

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://lore.altlinux.org/org.altlinux.lists.devel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git