Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Performance v's Maintainability

by Ryszard (Priest)
on Sep 22, 2002 at 12:39 UTC ( [id://199884]=perlmeditation: print w/replies, xml ) Need Help??

I’ve got a tiered architecture that is based on CGI::Application. Each application has a slew of modules it may use to achieve its result. There is also a heavy reliance on a database backend (maintaining state, reporting et al). Obviously there is also a heavy reliance on CGI.

The architecture is essentially two tiers:

  1. the Interface to the browser and the business logic
  2. the interface to the backend

I’m wondering the best method of implementing this style of application. Should each module be essentially standalone? By that I mean should each backend module create its own db handle (if required), and other object instances that may be used more than once by other modules called by the "interface" tier?

Or

To conserve memory, cpu cycles, and other overhead (ala dbi instances), should I pass pre-created objects (at the upper tier) down to the bottom tier?

I guess I’m looking at a trade off here. Performance v’s maintainability.

I’m kind of leaning toward pre-creating objects and passing them around, simply because of the performance aspect. For example I’ve an application that would open and close four different DBI objects to produce a page. Creating one DBI instance with a DB handle and handing it out will obviously be quite a bit faster than creating it 4 different times.

The application is on apache, but not mod_perl (yet), so from a purely database perspective the answer is to put DBI into every module that needs it once we get to mod_perl. However for other objects (CGI and others) would it be better to precreate them and pass them about? Or to make each backend module completely self sufficient?

Replies are listed 'Best First'.
Re: Performance v's Maintainability
by Aristotle (Chancellor) on Sep 22, 2002 at 13:08 UTC

    You can have your cake and eat it, too. If you abstract the persistence code into a class of its own, you can pass instances of this around without tying your framework to any specific persistence media. You just (a big just, though) have to define a consistent interface to your persistence class and can then hide access to a certain variety of SQL database, flatfiles, DBMs or whatever you may wish to use inside it, without giving up any maintainability. In fact, you're actually gaining another degree of maintainability. The extra layer costs some performance over the hard-wired approach of course, but it can be a relatively thin layer with only minor impact, and if your app is anywhere near as I/O-heavy as one might suspect by your description, it won't make any real difference.

    "Every problem in computing can be solved by adding another layer of indirection."
    (Another quote I'd love to know the originator and exact phrasing of.)

    Makeshifts last the longest.

      ...solved by adding another layer of indirection
      There is one problem not solvable this way: if you have too many layers of indirection... ;-)

      I recall a discussion in PM about solving problem by adding layers of indirection about a year ago.

      pmas
      To make errors is human. But to make million errors per second, you need a computer.

Re: Performance v's Maintainability
by perrin (Chancellor) on Sep 22, 2002 at 14:22 UTC
    There's another reason why you shouldn't put this stuff in more than once place: redundancy. You want to have the code that knows how to do each thing in only one place. This is the "once and only once" rule.

    What you can do is make a function that returns a valid database handle and call that from each of your modules. You can have this function do its own caching of the database handle for now (just stuff it into a global) and then when you switch to mod_perl you'll be able to change it to use Apache::DBI.

      Yeah, good call. What we've done (literally) is write a module that inherits all of CGI::Application's methods, (use base), then adds a few extra accessor methods its self. The .pm application you then would write in a standard C::A way uses our abstraction rather then C::A.

      So I guess in a way we have that abstraction layer Aristotle mentioned.. It does however feel messy to pass around objects, when, in purity I feel each module should survive stand-alone.

      I wonder if there is something i'm just not getting..

Re: Performance v's Maintainability
by disciple (Pilgrim) on Sep 22, 2002 at 15:44 UTC

    I have never implemented this in Perl, and I am a Perl newbie, but have been programming in other languages for a few years. If someone else mentioned this then, sorry, I didn't understand it.

    A common method of abstracting the database layer in other languages is to implement a class(module) that contains all database access code. This class acts as a mediator between your application code and the database. Do you need to get some user information from the database, call a method of the db class. How about updating the users information, call a method of the db class, etc... This provides the following benefits:

    • The rest of your application does not care where the data is stored or how the module gets it. All it has to do is call the appropriate method.
    • If you need to change your data access code, it is all encapsulated in one class.
    • The class can be standalone.
    • The DB handle is only created once, if you want.

    One pitfall is the added complexity. I have found debugging this type of architecture is more complicated than its flatter counterpart; but I think the benefits normally outweigh the consequences.

    I would be interested in knowing if anyone has implemented this in Perl and how it worked out for them. Even if you haven't implemented it, I would like to know if there are any reasons why this wouldn't be w good idea in Perl (I will be considering this architecture in the near future).

    -disciple

    I am not even sure if it is appropriate that I attempt to comment on your post. It would seem to me that you probably already know what I wrote.

      That's what I posted, though you explained more verbosely and in-depth. :-)

      Makeshifts last the longest.

        There is something to be said for brevity. Unfortunately, I was too dense to understand it. :-)
Re: Performance v's Maintainability
by chromatic (Archbishop) on Sep 22, 2002 at 17:02 UTC

    I almost never ask "Which approach has better performance?" I've been developing a feel for that anyway. I almost always go for code simplicity and maintainability. As perrin says, make sure you stick with Once and Only Once.

    If you're having measurable performance issues after that, profile. Otherwise, the best we can do guess. I tend to be wildly wrong when playing guess the bottleneck.

Re: Performance v's Maintainability
by PodMaster (Abbot) on Sep 22, 2002 at 13:09 UTC
    Well there's some things you shouldn't do using the database, like maintaining sessions. You really ought to use something like File::Cache or one of the Cache::Cache modules.

    Do you do any forking? If not I say passing around a DBI handle is pretty dang reasonable. That way when you go to mod_perl, and use Apache::DBI (which you probably will), you get the performance benefit.

    BTW: I like DBIx::DWIW ;)

    update: When I say maintaining sessions, I don't mean maintaining some kind of database (like a playlist), I mean managing data that has a short lifespan, that's supposed to expire and go away.

    ____________________________________________________
    ** The Third rule of perl club is a statement of fact: pod is sexy.

      Well there's some things you shouldn't do using the database, like maintaining sessions.

      This doesnt quite ring true to me. I'm assuming you're talking about performance...? However I'm interested to know how you correlate user data back to a user without using the session_key as the identifier of the user...

      I have a setup where a user may store personal data on website (for example a playlist). I use an RDMBS as the storage mechanism as it is robust, relatively fast and i can port my SQL92 complient application to another RDMBS relatively painlessley. For the user to edit that bit of data (for me to retrieve the correct data) I correlate the session_id to a user_id via sql...

      OT to the original post, i'm interest in what mechanism you use, or would recommend..

      There's nothing wrong with storing sessions in a database. If you have a cluster of servers, it's the simplest way to go.

      I'm sure this strategy of doing session or other short-lived stuff out of the database works for smaller stuff (like mine) but there *is* a definate cost to iterate through a directory entry. Your database and operating system have all kinds of logic to cache things in memory that aren't going to be used if you do something simpler. It's something to keep in mind anyway.

Re: Performance v's Maintainability
by Phaysis (Pilgrim) on Sep 22, 2002 at 20:36 UTC
    What I'm doing with my website engine is what others have mentioned: I'm segmenting core functionalities into their own modules, complete with their own accessor methods, as a layer of abstraction between my application and the appropriate CPAN modules (CGI::, DBI::, etc). My aim is to create a Site::Request object, which is an interface to the CGI:: module, pass the reference of that to the Site::Auth object (which manages session state and user permissions), which creates the Site::SQL object, which in turn creates the DBI:: object. The core kernel code then passes the references to the ::Request and ::Auth objects to my templating subsystem module, Site::Template, to produce the output.

    Currently, glacial development speed notwithstanding, it appears to me to be a valid and pliable approach. I'm considering ways to incorporate other tertiary modules for usage into the templating subsystem, like page creation and modification tools, without having to use standalone scripts in something like a seperate document subdirectory.

    I suppose each of my modules could be considered standalone in functionality and abstraction, but each relies on the successful creation and execution of the previous modules. It seems the most sensible thing to me. I'm trying to make this code as portable and flexible as possible; I'm currently on a non-mod_perl web host, sharing resources with other domain owners, but down the road things may change. Having to switch over from MySQL to PostGreSQL should be easier this way. With the way the modules themselves are written, there are very few non-constant globals (everything else is in object instances), so the switchover to a mod_perl environment should, in theory, be painless as well. One could hope, right?

    -Shawn / (Ph) Phaysis
    If idle hands are the tools of the devil, are idol tools the hands of god?

      I suppose each of my modules could be considered standalone in functionality and abstraction, but each relies on the successful creation and execution of the previous modules.

      Which is kind a what I'm doing...

      Lets say I have two modules A and B. Both these modules are part of the lower layer described in my orig. post. Lets say Module A goes and fetches some user information from an RDMBS and module B goes and fetches some information from an RDBMS. Both modules are abstracted to their respective domains (classes), both fetch different bits of non related information.

      For maintainability its easier to have both modules A and B create their own db handle fetch the data and return it to the upper layer... for Performance it would be better for the upper layer to pass a pre-created db handle (object) down to the bottom layer - which makes modules A and B not stand alone (or self sufficient). This need not apply exclusively to DB handles (I chose this example because of the overhead in creating/destroying DB handles), but any "helper" module that may be used more than once by the lower layer.

      What I’m hearing thus far, is maintainability matters, of course it does, but so does performance.. hence the trade off.. ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://199884]
Front-paged by TStanley
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (None)
    As of 2024-04-25 04:04 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found