Re: RFC: Time Lock Module Idea?
by davidrw (Prior) on Mar 24, 2006 at 19:56 UTC
|
Two (albeit quick & dirty and maybe not as fun as a new module ;) ) alternatives pop into mind:
- Just a pid-file type thing and check the timestamp:
my $lock = "/tmp/lock_foo";
if( ! -f $lock || time - (stat($lock))[9] > 600 ){
# do stuff
open FILE '>', $lock;
# optionally write info/details to it
}
- Use Cache::FileCache (or Storable, etc):
use Cache::FileCache;
my $cache = new Cache::FileCache( { namespace=>"TimeLocks" } );
my $key = "something_bad";
if( ! $cache->get($key) ){
# do stuff
$cache->set( $key, time, "10 minutes" ); # note the value could jus
+t be '1', or any other true value.
}
| [reply] [d/l] [select] |
Re: RFC: Time Lock Module Idea?
by rhesa (Vicar) on Mar 24, 2006 at 20:19 UTC
|
Hi pileofrogs,
I like the idea, definitely. I get swamped with pages/error emails/you name it on occasion, and would love a good solution to that problem. I was actually a bit surprised I couldn't find anything on CPAN that already does this, so yes, please, build this *now* :)
As for the name, TimeLock doesn't really explain what it does. I'm not really good with names, but maybe something like "Sub::Deferred" would be clearer.
I feel your interface is too verbose, and interferes with the flow of the code. I would recommend an interface like Memoize, in that you "defer" a function or method somewhere at the top of your script, while the rest of your code stays as is:
use Sub::Deferred store => '/tmp/foo_timelock';
defer( 'page_me', 600 );
# ...
if( something_really_bad() ) {
my $message = "Something Really Bad just happened...";
page_me( $message ); # run only once every 600 seconds
}
This way you could add it to existing scripts without having to go through a lot of code. And it's very easy to disable too.
It would probably be quite easy to decide to defer a subroutine based on its input parameters. Strategies and settings could be specified in the call to defer().
| [reply] [d/l] [select] |
|
I like the name idea, but I'm skeptical of the interface suggestions. The code would lose readability and my module would be a lot more complex. In Memoize, you don't lose readability because a memoize'd sub still does the same stuff regardless of weather it's cached or not. In this application the behavior is different. I can see a code maintainer going nuts because this thing only sends an email every 5 minutes, no matter what he does and he doesn't know why until he sees that defer() call at the top.
If there's demand for it, though, I'd be happy to write it with both interfaces.
Can you think of a way to make it more compact without losing readability?
Anyone else have thoughts on that?
| [reply] |
|
| [reply] |
Re: RFC: Time Lock Module Idea?
by Tanktalus (Canon) on Mar 24, 2006 at 22:05 UTC
|
You have two aspects here that I see. First is an API. Second is an implementation. This may not sound too useful as most modules have an API, and implement that API. However, I want to seperate these out because I think that once there is a set way of doing this, I can still imagine many people wanting to do it differently under the covers.
For example, you talk about GDBM. What if I don't have GDBM capabilities? Perhaps another implementation would use a directory passed in as part of the constructor to hold lock files. Or another would use DBI to store in a RDBMS. Or another would use shared memory. Which backend to use would be part of your constructor.
As for the API, I would suggest some minor changes:
- new(driver => $driver, ...)
- The parameters would first be which driver to use, which could default to your filesystem-based one. Other parameters would be passed in to that. Perhaps the string would be the name of the module, and that might be all relative to "Sub::Deferred", e.g., $driver as "Filesys" would actually load "Sub::Deferred::Filesys". If the object is a reference, assume it has already been initialised, and use it.
- $lock = get_lock($key)
- Returns a Sub::Deferred::Lock object. Does not set, reset, or anything else, for the lock. Just retrieves a handle.
- list_keys()
- Some way to list what locks are in use. The precise definition of this is somewhat ambiguous, though, since once a lock expires, it may get cleaned up. I suppose the caveat here is that just because it's in the list doesn't mean it's locked, and just because it's not in the list doesn't mean it has never been used.
- $lock->set($span)
- What is $span? Seconds? Milliseconds? Hours? Parsed somehow? Could you accept a DateTime object or something to specify the lock-until time instead? Or some other object(s) that specify a span of time?
- $lock->is_locked()
- Boolean methods/functions should start with "is" or something to show it's a boolean query function.
- $lock->time_left()
- Some way to query when the lock will be freed is probably nice. Should return 0 if the lock isn't set.
- $lock->reset()
- Immediately resets the lock to "unlocked". Trust me, it'll be handy ;->
Just my two cents.
| [reply] [d/l] [select] |
Re: RFC: Time Lock Module Idea?
by davidrw (Prior) on Mar 25, 2006 at 02:36 UTC
|
I mentioned above that you can basically accomplish your sample code w/Cache::FileCache .. i was still pondering this in the back of my head and searched cpan for "lock" and found IPC::Lock with also can do (in a different way--you have to subclass it) the same thing ... | [reply] |
Re: RFC: Time Lock Module Idea?
by zentara (Cardinal) on Mar 25, 2006 at 11:55 UTC
|
Something about this "timelock" sounds suspiciously like an event-loop monitoring somevalue, see Roll your own Event-loop. What I would do, is setup an event loop, and when a page or email notification is sent, set a flag to 1, and start a timer, which will reset the flag to zero after a time period. POE could also be used for this, but Glib is all you need. There are also some "event" modules on CPAN. But we all approach problems in terms of the tools which we are used to using.....your database idea would work too, but it seems more complicated than it needs to be, to get the job done.
I'm not really a human, but I play one on earth.
flash japh
| [reply] |
|
I think the key difference here is that an event loop is only good inside a single process (possibly with threads). While something external, whether that be filesystem, shared memory, database, whatever, can span processes.
For example, a cron job that runs every 30 seconds to ensure system availability. If something goes down, we want to page the sysadmin. But we don't want to page the sysadmin every 30 seconds, we want to wait for the next page for at least, say, 60 minutes. Simply check for the lock - if it's locked, don't page. If it is unlocked or reset, lock it and send the page. Then your cron job can exit, knowing that the next invocation will remember this.
A database has some additional benefit: you can have multiple machines doing the monitoring with some shared semaphores. Say machine A is monitoring 1, 2, and 3. And machine B is monitoring 4, 5, and 6. When something goes wrong with 2, machine A will send an email describing the problem to the sysadmin, and page her. Two locks will be set: one for email and one for paging. Then something goes wrong with 6, only 10 minutes later. An email gets sent out, but machine B will "notice" that the page was sent within the last 60 minutes and not send out a new page (yet). And, of course, if the sysadmin gets the first email within the 10 minutes, she could hit a switch somewhere to reset the page lock so she would get the new page when something went wrong with 6. (I said it would come in handy ;->) A filesystem would work, too, but locking across NFS or SMB is much more tricky than letting the DB handle it for you.
So I'd have to say that the database idea may not be more complicated than it needs to be - depending on the job. TMTOWTDI - but there's also more than one problem each way may be able to solve ;-)
| [reply] |
|
But we don't want to page the sysadmin every 30 seconds, we want to wait for the next page for at least, say, 60 minutes. Simply check for the lock - if it's locked, don't page. If it is unlocked or reset, lock it and send the page. Then your cron job can exit, knowing that the next invocation will remember this.
Be careful under which circumstances you reset the pager.
One of my friends once used a simple: "email me at first occurance of the problem, and later if someone has fixed it". Then he hit an intermittent bug that toggled the "it works now/it's broken now" status every few minutes, and his mailbox was full of worthless messages. I shudder to think how annoying that would have been if rigged to a pager...
Just a consideration to keep in mind...
--
Ytrew
| [reply] |