Background: Recently, the perl based system I am responsible for had a production issue when new code was being rolled out. It is our desire to never fully crash due to some state information that would be lost that is too transient to effectively serialize to disk with any level of efficiency. It was decided to simply increase the amount of exception handling within the core routines, so even if an issue was encountered, it could be isolated, and even repaired while the system remained running. We have full regression and unit tests for our system, which posed an interesting problem for me. How do you run your production code, and verify that it can handle any part of it's code throwing an exception, without breaking any test after this one will run (and not modifying the code BEFORE the test is run). The solution that I came up with, is grounded in the flexibility of our testing framework, so I welcome any alternative solutions, just understand that I'm not filling you in on all the constraints I'm working within. Here is a code snippet to illustrate the approach I took. (keep in mind that I understand that the "eval" is not needed, I built this test to try and more accurately model my system specifically)
#!/usr/bin/perl package test_package; use strict; use warnings; sub new { return bless {}; } sub test_function { print "Original test_function! $_[1]\n"; } sub loader { eval($_[1]); warn "ERROR: $@\n" if ( $@ ); } package main; use strict; use warnings; my $counter = 0; my $f = new test_package(); $f->test_function($counter++); # 1 # "backup" the test_function method $f->loader('*test_package::new_test_function = \&test_package::test_fu +nction;'); $f->new_test_function($counter++); # Overwrite the existing "test_function" subroutine $f->loader("package test_package; sub test_function { print \"New outp +ut! \$_[1]\n\"; } 1;"); # Should see two different outputs $f->test_function($counter++); $f->new_test_function($counter++); # "Restore" the test function from it's "backup" $f->loader("*test_package::test_function = *test_package::new_test_fun +ction;"); $f->test_function($counter++);
The output looks like this:
Original test_function! 0 Original test_function! 1 Subroutine test_function redefined at (eval 2) line 1. New output! 2 Original test_function! 3 Original test_function! 4
Using this approach allowed me to do the following: All in all, this approach worked for me, and although it may be common knowledge to some, it wasn't something I found documented on the web, or even within perlmonks. I hope it comes in useful in the future.