Limbic~Region has asked for the wisdom of the Perl Monks concerning the following question:

All,
I am currently writing an network application that requires the ability to modify the running code without dropping connected users. I have already split the package across several files. This makes maintenance easier as well as allows for parts of the code to be re-loaded individually. Here is an example:

Script that uses the module

#!/usr/bin/perl use strict; use warnings; use Cool; my $obj = Cool->new(); while (1) { print $obj->testit(), "\n"; sleep 1; $obj->soft_boot(); }

The module itself

package Cool; use strict; use warnings; require 'magic'; 'This statement is false';

The file called 'magic'

package Cool; use strict; use warnings; no warnings 'redefine'; sub new { return bless {}, $_[0]; } sub testit { return 'Change me and see what happens'; } sub soft_boot { do 'magic'; } 'This statement is false';

Given this bizarre situation, is this the sanest way you can think to do it? If not, please offer your advice (preferrably with a working code snippet).

And yes diotalevi, I know this blows my method cache

Cheers - L~R

Replies are listed 'Best First'.
Re: Sanely modifying running code
by kvale (Monsignor) on Mar 14, 2006 at 01:38 UTC
    Isolating self-modifying code is good. Then quietly strangling it is even better.

    More seriously, the combination of network application and self-modifying code is a dangerous one. I would add taint detection and use a whitelist scheme to control input.

    If there are only a small number of different possible modifications, derive subclasses of Cool with each possible modification. Another possiblity is to implement a plugin architecture so that new(), testit(), and soft_boot() can be different for each plugin, but still have a standardized interface.

    -Mark

      kvale,
      Thanks for the input. To address some of your concerns, code is modified by hand by editing the file. The administrator is then able to bring his changes on line by issuing a secured command while connected and the code updates itself. Yes - still dangerous.

      I am implementing my own variation of an network application that has been around for a going on 2 decades (similar to MUDs). This is for learning purposes only and the actual code may not see the light of day.

      Cheers - L~R

Re: Sanely modifying running code
by thor (Priest) on Mar 14, 2006 at 01:35 UTC
    What if you had two instances of your service running and had a way to individually control the starting/stopping of each one? If you did this, you could shut down service A, change whatever you want, bring up service A, and then do the same for B. The "stop" command would really be a quiesce, allowing currently connected users to stay connected, but new requests would go to the other service. Once all the currently connected users disconnect, the service shuts down and you can do your work. This assumes that user sessions are relatively short-lived.

    thor

    The only easy day was yesterday

      thor,
      Hmmm - what you describe appears to assume that my network application is web based where HTTP is stateless and "sessions" are created through the application. This is not the case - these are sockets. A TCP connection is established and remains that way until termination. Building the necessary framework to make your suggestion work is way beyond the scope of this project. If I am missing something obvious - let me know.

      Cheers - L~R

        what you describe appears to assume that my network application is web based where HTTP is stateless and "sessions" are created through the application.
        Almost. It does, however, assume that sessions are started and stopped relatively quickly (think minutes instead of days).
        A TCP connection is established and remains that way until termination.
        Interestingly enough, that's how the web works. ;) Either way, you're free to use the methodology that I stated above or not; I won't be hurt if you don't.

        thor

        The only easy day was yesterday

Re: Sanely modifying running code
by spiritway (Vicar) on Mar 14, 2006 at 02:14 UTC

    Modifying running code is bad juju, IMNSHO.

      I'd have to agree. However, due to politics or financial considerations, there may not be another palatable option, e.g. PHBs may insist that you do it in a production environment to get a 'real feel' for what it's like, and/or you may not have money or authorization to have a test machine.

      That said, if management and users are OK with you doing this, and you don't have a test server, it may be a matter of the lesser of two (or more) evils. But again, I'd say try and do this in an isolated test environment first, if at all possible - you can create test accounts and log them in, and test your code to make sure the users don't get disconnected.

      -- Burvil

      spiritway,
      It sure is - and? IMO, your answer is like telling teenagers not to have sex because it is bad. The reason for asking the question is to understand what can go wrong, what the alternatives are, and what is the "best" you can do to mitigate risks if you choose to proceed. While in YNSHO, any use of modifying code is insane, I am asking what is the relatively sanest way about doing it.

      Cheers - L~R

        Hi, [id://Limbic~Region]. I think the analogy isn't so much like telling teenagers sex is bad (which, in fact, it isn't), as much as it is saying something like, "sticking your finger in a light socket is not a good idea". For one thing, the payoff is far less rewarding. And the possible downside becomes apparent almost immediately.

        I guess I don't really see a "best" way to do something that, as far as I can see, is neither necessary nor particularly safe; that has no payoff I can even imagine (which doesn't mean it's not there - it could be my lack of imagination).

        And no, I don't consider it *insane* - just leaving yourself open to a world of troubles. I've heard of some amazing things done by people who broke this sort of rule and managed to do it without wreaking havoc. So I guess it can happen - and I wish you the best if you want to do that.

Re: Sanely modifying running code
by QM (Parson) on Mar 14, 2006 at 16:49 UTC
    I don't know what the requirements are, but here are some random thoughts...

    Would it make more sense to have a babysitter app running that launches the working app, checks for updates, signals the old app to stop accepting new work (and terminate, if that's appropriate), and launches a new updated app?

    Given your suggested approach, what are the implications if there's a bug in the update?

    Can the new functionality be returned as a data structure? If the file is updated, run it to get the new data structure. Run some tests against it, then replace the old data structure with the new. On the other hand, if the tests fail, keep the old data structure. (Replacing symbol table entries in a module is easy -- replacing other stuff is problematic.)

    -QM
    --
    Quantum Mechanics: The dreams stuff is made of

      QM,
      The requirements are that I can't drop connected users while still changing the code. I could make a proxy application which doesn't really do anything except persist the connection. It would have the ability to switch which backend process it interacts with. Then it is a simple matter of bringing up a new instance of the process and signaling the proxy app to switch. This is a really neat idea and I will try it out eventually.

      As far as problems with the current implementation - things could go boom if there is a bug in the update. There are ways of removing compile time bugs by making everything code refs and then eval'ing them before switching but that won't fix run-time bugs.

      Cheers - L~R

        There are ways of removing compile time bugs by making everything code refs and then eval'ing them before switching but that won't fix run-time bugs.
        I was hinting at running tests on the new stuff before it's accepted by the proxy process and supercedes the old stuff. Something like
        if (check_for_new_stuff($new_stuff_location)) { my $new_stuff = do $new_stuff_location; if (run_time_tests($new_stuff)) { $old_stuff = $new_stuff; } }

        -QM
        --
        Quantum Mechanics: The dreams stuff is made of