Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Better "IPC" method than BDB?

by bennymack (Pilgrim)
on May 18, 2007 at 16:10 UTC ( [id://616226]=perlquestion: print w/replies, xml ) Need Help??

bennymack has asked for the wisdom of the Perl Monks concerning the following question:

Hello hallowed monks of the monastery

I'm working on an server app that needs to be able to receive and store realtime updates and make the updates available for all child processes.

I chose BerkeleyDB initially because it's fast and supposed to be reliable. I'm just wondering what a better way to accomplish this might be.

My current setup filters ALL set and remove operations through one process due to the fact that when more than one process does them, deadlocks occur rather often. This setup hasn't deadlocked yet.

The problem with this setup is that, while it works, it was a major pain to get right what with all the different processes and cache handles floating around. So, should I look at something else or not fix it if it isn't broken?

#!/usr/bin/perl # test_bdb_1.pl use strict; use warnings; use Carp; use POSIX; use Socket; use BerkeleyDB; use Getopt::Long; use IO::Socket::INET; my %opts; GetOptions( \%opts, 'start_listener', 'start_server', 'view_message', 'send_message=s', ); sub cleanup { warn 'Caught SIG', $_[0]; kill 'TERM', -$$; } sub goaway { warn 'Caught SIG', $_[0]; kill 'HUP', -$$; } my $cache_dir = '/tmp'; my $cache_file = 'test_cache'; sub get_cache { my $env = BerkeleyDB::Env->new( -Home => $cache_dir, -Flags => DB_CREATE| DB_INIT_CDB | DB_INIT_MPOOL, ); my $cache = BerkeleyDB::Btree->new( -Filename => $cache_file, -Flags => DB_CREATE, -Env => $env, ); return $cache; } if( $opts{start_listener} ) { fork and exit; POSIX::setsid; if( not fork ) { unlink( map { "$cache_dir/$_" } ( $cache_file, map { sprintf( +'__db.%0.3d', $_ ) } ( 1 .. 5 ) ) ); my $cache = get_cache; my $socket = IO::Socket::INET->new( Proto => 'udp', LocalAddr => '127.0.0.1:12345', ReuseAddr => 1, ) || Carp::confess 'No $socket: ', $!; while( 1 ) { $socket->recv( my $message, 4096, ); my $time = time; warn $$, ' adding message ', $message, ' time ', $time; my $cds_lock = $cache->cds_lock; $cache->db_put( $message, $time ); $cache->db_sync; } warn 'start_listener child exiting...'; exit; } local $SIG{TERM} = \&goaway; local $SIG{INT} = \&cleanup; local $SIG{__DIE__} = \&cleanup; 1 while wait != -1; warn 'start_listener parent exiting...'; exit; } elsif( $opts{start_server} ) { fork and exit; POSIX::setsid; if( not fork ) { my $socket = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:54321', Listen => 1, ReuseAddr => 1, ) || Carp::confess 'No $socket: ', $!; my $cache = get_cache; while( 1 ) { my $client = $socket->accept; my $output = "$$\n"; my $cursor = $cache->db_cursor(); my( $k, $v ) = ( 0, 0 ); while( $cursor->c_get( $k, $v, DB_NEXT ) == 0 ) { $output .= "$k -> $v\n"; } $client->send( $output ); } warn 'start_server child exiting...'; exit; } local $SIG{TERM} = \&goaway; local $SIG{INT} = \&cleanup; local $SIG{__DIE__} = \&cleanup; 1 while wait != -1; warn 'start_server parent exiting...'; exit; } elsif( my $message = $opts{send_message} ) { my $socket = IO::Socket::INET->new( Proto => 'udp', PeerAddr => '127.0.0.1:12345', ReuseAddr => 1, ) || Carp::confess 'No $socket: ', $!; $socket->send( $message || 'default message' ); } elsif( $opts{view_message} ) { my $socket = IO::Socket::INET->new( PeerAddr => '127.0.0.1:54321', ) || Carp::confess 'No $socket: ', $!; $socket->recv( my $message, 4096 ); print $message, "\n"; }

And then to run it:

$ perl test_bdb_1.pl -start_listener $ perl test_bdb_1.pl -start_server $ perl test_bdb_1.pl -send_message hello 13854 adding message hello time 1179504307 at test_bdb_1.pl line 67. $ perl test_bdb_1.pl -view_message 13857 hello -> 1179504307

Which works fine. But keep in mind that for my purposes I need to have this be rock-solid under extremely high numbers of processes reading from the cache.

Thanks for any advice you may have!

Replies are listed 'Best First'.
Re: Better "IPC" method than BDB?
by perrin (Chancellor) on May 18, 2007 at 16:26 UTC
    I'm not aware of anything with better performance than BerkeleyDB. However, getting it right is not easy, as you say. If you can sacrifice a bit of speed for something simpler, DBI to a MySQL server running on localhost is nearly as fast and somewhat easier to understand.
Re: Better "IPC" method than BDB?
by zentara (Archbishop) on May 18, 2007 at 20:12 UTC
      Yikes. Be sure to Benchmark that! My experience with SysV shared memory is that it's not very fast at all - generally about the same as a disk-based cache. Non-intuitive, I know, but that's performance tuning for you!

      -sam

        Well, how can IPC where ram memory is actually shared between apps be slower? It must be in your implementation or your kernel. I agree though, it can be tricky to use. Read Chapter 5 of ALP

        I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Better "IPC" method than BDB?
by derby (Abbot) on May 18, 2007 at 16:27 UTC
      BDB is a lot faster than memcached. A local MySQL server is about the same speed as memcached, but since it's a database it doesn't drop data when you shut it down or it hits the edge of the memory you allocated to it.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://616226]
Approved by derby
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (5)
As of 2024-04-18 05:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found