He tenido algunos problemas con el uso de BerkeleyDB. Tengo varias instancias del mismo código apuntando a un único repositorio de archivos de base de datos, y todo funciona bien durante 5-32 horas, luego, de repente, hay un interbloqueo. Las instrucciones del comando se detienen justo antes de ejecutar una llamada de creación de cursor o db_get o db_put. Así que simplemente estoy preguntando por la forma correcta de manejar estas llamadas. Aquí está mi disposición general:¿Cuál es la forma correcta de acceder a BerkeleyDB con Perl?
Así es como se crean el medio ambiente y DB:
my $env = new BerkeleyDB::Env (
-Home => "$dbFolder\\" ,
-Flags => DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL)
or die "cannot open environment: $BerkeleyDB::Error\n";
my $unsortedHash = BerkeleyDB::Hash->new (
-Filename => "$dbFolder/Unsorted.db",
-Flags => DB_CREATE,
-Env => $env
) or die "couldn't create: $!, $BerkeleyDB::Error.\n";
Una sola instancia de este código se ejecuta, va a un sitio y guarda las direcciones URL para ser analizados por otra instancia (no tengo el conjunto de bandera de modo que cada DB está bloqueado cuando uno está bloqueado):
$lk = $unsortedHash->cds_lock();
while(@urlsToAdd){
my $currUrl = shift @urlsToAdd;
$unsortedHash->db_put($currUrl, '0');
}
$lk->cds_unlock();
se comprueba periódicamente si un cierto número de artículos están en Unsorted:
$refer = $unsortedHash->db_stat();
$elements = $refer->{'hash_ndata'};
Antes de añadir cualquier elemento a cualquier DB, primero comprueba todos los DBs para ver si ese elemento ya está presente:
if ($unsortedHash->db_get($search, $value) == 0){
$value = "1:$value";
}elsif ($badHash->db_get($search, $value) == 0){
$value = "2:$value";
....
Este siguiente código viene después, y muchos casos de que se ejecutan en paralelo. Primero, obtiene el siguiente ítem sin clasificar (que no tiene el valor ocupado '1'), luego establece el valor en ocupado '1', luego hace algo con él, luego mueve la entrada DB completamente a otro DB (es removidos de clasificar y almacenar en otro DB):
my $pageUrl = '';
my $busy = '1';
my $curs;
my $lk = $unsortedHash->cds_lock(); #lock, change status to 1, unlock
########## GET AN ELEMENT FROM THE UNSORTED HASH #######
while(1){
$busy = '1';
$curs = $unsortedHash->db_cursor();
while ($busy){
$curs->c_get($pageUrl, $busy, DB_NEXT);
print "$pageUrl:$busy:\n";
if ($pageUrl eq ''){
$busy = 0;
}
}
$curs->c_close();
$curs = undef;
if ($pageUrl eq ''){
print "Database empty. Sleeping...\n";
$lk->cds_unlock();
sleep(30);
$lk = $unsortedHash->cds_lock();
}else{
last;
}
}
####### MAKE THE ELEMENT 'BUSY' AND DOWNLOAD IT
$unsortedHash->db_put($pageUrl, '1');
$lk->cds_unlock();
$lk = undef;
y en cualquier otro lugar, si llamo db_put o db_del en cUALQUIER DB, se envuelve con una cerradura de este modo:
print "\n\nBad.\n\n";
$lk = $badHash->cds_lock();
$badHash->db_put($pageUrl, '0');
$unsortedHash->db_del($pageUrl);
$lk->cds_unlock();
$lk = undef;
Sin embargo , mis comandos db_get flotan libremente sin bloqueo, porque no creo que la lectura necesite un bloqueo.
He revisado este código un millón de veces y el algoritmo es hermético. Entonces, me pregunto si estoy implementando alguna parte de este error, utilizando mal las cerraduras, etc. O si hay una forma mejor de evitar el bloqueo (o incluso diagnosticar el bloqueo) con BerkeleyDB y Strawberry Perl.
ACTUALIZACIÓN: Para ser más específicos, el problema se produce en un servidor Windows 2003 (1,5 GB de RAM, no estoy seguro si esto es importante). Puedo ejecutar bien toda esta configuración en mi máquina con Windows 7 (4 GB de RAM). También empecé a imprimir las estadísticas de bloqueo utilizando la siguiente:
La adición de este indicador a la creación medio ambiente:
-MsgFile => "$dbFolder/lockData.txt"
Y luego llamar a esto cada 60 segundos:
my $status = $env->lock_stat_print();
print "Status:$status:\n";
el estado es siempre devuelto como 0, que es éxito.Aquí está el último informe de estadísticas:
29 Last allocated locker ID
0x7fffffff Current maximum unused locker ID
5 Number of lock modes
1000 Maximum number of locks possible
1000 Maximum number of lockers possible
1000 Maximum number of lock objects possible
40 Number of lock object partitions
24 Number of current locks
42 Maximum number of locks at any one time
5 Maximum number of locks in any one bucket
0 Maximum number of locks stolen by for an empty partition
0 Maximum number of locks stolen for any one partition
29 Number of current lockers
29 Maximum number of lockers at any one time
6 Number of current lock objects
13 Maximum number of lock objects at any one time
1 Maximum number of lock objects in any one bucket
0 Maximum number of objects stolen by for an empty partition
0 Maximum number of objects stolen for any one partition
3121958 Total number of locks requested
3121926 Total number of locks released
0 Total number of locks upgraded
24 Total number of locks downgraded
9310 Lock requests not available due to conflicts, for which we waited
0 Lock requests not available due to conflicts, for which we did not wait
8 Number of deadlocks
1000000 Lock timeout value
0 Number of locks that have timed out
1000000 Transaction timeout value
0 Number of transactions that have timed out
792KB The size of the lock region
59 The number of partition locks that required waiting (0%)
46 The maximum number of times any partition lock was waited for (0%)
0 The number of object queue operations that required waiting (0%)
27 The number of locker allocations that required waiting (0%)
0 The number of region locks that required waiting (0%)
1 Maximum hash bucket length
de la que soy cuidado con esto:
8 Number of deadlocks
¿Cómo se producen estos callejones sin salida, y cómo se resolvieron? (todas las partes del código todavía están en ejecución). ¿Qué es exactamente un punto muerto, en este caso?
Estoy un poco confundido acerca del trabajo de Mutex, especialmente en relación con Berkeley, ¿podría dar un ejemplo que coloque un artículo en un DB usando el Mutex? ¿Cómo evita que otros procesos accedan al DB? –
Es una "cosa" que tienen las ventanas, que cualquier subproceso en cualquier proceso en la máquina puede intentar bloquear. La llamada a la espera() bloquea hasta que el hilo tenga el bloqueo. Luego, harías todo el acceso a tu base de datos en lugar de mi llamada de sleep(). Esencialmente haces el bloqueo en lugar de BDB. Intenta ejecutar un par de procesos del script de arriba para ver cómo funciona. – Alex