Insert & Update

The ORM docs have a basic example and that pretty much sums it up. It’s that simple. ->save() inserts or updates. Which it does depends on the primary key. It will update if the primary key has been set but has not been changed. Otherwise it’s an insert. I’ll drop in their example for illustration:

$user = ORM::factory('user', 1);
$user->name = 'Joe';
$user->save();

Occasionally you need to do slightly different logic based on insert or update, perhaps you need to generate a key or log a change. Couldn’t be easier, just override the save() function in your model and steal the test the orm uses to determine insert/update.

    public function save()
    {
        // if it's an insert...
        if ($this->empty_pk()
            || isset($this->_changed[$this->_primary_key])) {
            ... generate account key ...
            $this->last_modified = date('Y-m-d h:i:s');
        }
        return parent::save();
    }

You may notice the assignment for last_modified. I’m not a fan of using the app server time in the db if I can avoid it (while I am very much a fan of using timestamp columns in mysql or postgres, please don’t talk to me about how I should be using an int column with time in seconds). Anyways. Much better imho to let the db set the timestamp (unless for some bizarre reason you need the app server time which could be wildly different from db time), but doing that requires something special…

Using Expressions in Writes

All values are safely quoted in insert and update queries, but occasionally you want the value to be evaluated by the database. Simply assigning the expression to a column, like $stats->count = ‘count + 1’ or $stats->last_modified = ‘now()’ will crash and burn. You need to use DB::expr to avoid the expression being treated as a string.

$stats = ORM::factory('stats', $id);
$stats->count = DB::expr('count + 1');
$stats-$gt;last_modified = DB::expr('NOW()');
$stats->save();

Keep in mind that it goes straight into the query as is, so it’s a dangerous spot to allow user input. You are responsible for your own escaping and quoting. Also, if you’ve got table/column aliases or joins going on in an update, it’s possible you may need to reference columns by full tablename.column_name syntax.

Form Creation

This one is a little odd to blend in here, but I thought it relevant to writing. If you are using sprig you are practically home free, because it has a built in way to generate form fields based on model field attributes. Nice. I don’t know how far you’d get without needing to extend sprig anyways just to add or adjust elements, but it’s a great timesaver regardless. If you just don’t want to use sprig, I recommend straight up stealing sprig’s style and extending ORM. That’s what I did anyways, as I have a theory that writing form elements repeatedly will make you dumber over time.

An option here is to use a protected array of _form_elements keyed by column name, with attributes needed for form fields, but I added to my definition of $_fields (first mentioned in the post on definition and connection) and used kohana’s form helpers (see the unofficial wiki and scroll down to the section for the Form Class).

field definition in model/mymodel.php:


    protected $_fields = array(
        'id' => array(
            'column_name' => 'admin_user_id',
            'type' => 'int',
            'label' => 'id',
        ),
        'type' => array(
            'column_name' => 'usertype_id',
            'type' => 'int',
            'label' => 'type',
            'form_element' => array('type' => 'select', 'options' => array(1=>'basic', 2=>'premium')),
        ),
        'parent_id' => array(
            'form_element' => array('type' => 'select', 'option_callback' => 'Model_Account::get_all_parents'),
        ),
        ...

orm/extension.php:

    public function input($column, $attributes = null)
    {
        // default to type='text'
        if (!isset($this->_fields[$column]['form_element'])) {
            return form::input($column, $this->$column, $attributes);
        }   

        $e = $this->_fields[$column]['form_element'];

        if (isset($e['option_callback'])) {
            list($class, $method) = explode('::', $e['option_callback'], 2);
            $method = new ReflectionMethod($class, $method);
            $e['options'] = $method->invokeArgs(null, array());
            if (!is_array($e['options'])) $e['options'] = array();
        }

        if (isset($e['options'])) {
            $e['options'] = array(''=>'') + $e['options'];
        }

        if ($e['type'] == 'select') {
            return form::select($column, $e['options'], $this->$column, $attributes);
        }   

        if ($e['type'] == 'password') {
            return form::password($column, null, $attributes);
        }
        .... other field types ....
    }

So if you pass your model to the view, you can plop down form fields like this:

views/create.php:

<div id='account_form' class='form_box'>
  <?php echo form::open(); ?>
  <div>parent account: <?php echo $account->input('parent_id') ?></div>
  <div>type:           <?php echo $account->input('type') ?></div>
  <hr>
  <div>name:  <?php echo $account->input('name') ?></div>
  ...

Model Data Validation (special guest: form validation)

This is quite the topic. Clearly a lot of people have their own opinions on just how this should be done, I’ll give you mine, and you can feel free to disagree. Rules on fields that are actually in your table are defined and checked in the model. Rules for fields that exist only in your form get validated by the controller. I’m not a fan of the ol’ stick-random-fields-in-table-columns-and-ignored-columns thing just for a form-specific email confirmation field. Ridiculous. You can run check on the model outside of save if it’s necessary for your controller to do so, however if no check has been run on save, check is run inside save(), and throws an exception on error. All of this is based on using the validation system kohana offers.

Example rules definition in model/mymodel.php (notice how the phone rule argument is passed as an array in an array, the docs would tempt you to define it incorrectly as a simple array):


    protected $_rules = array(
        'name' => array('not_empty' => null),
        'zip'         => array('regex'     => array('/^\d{5}(\-\d{4})?$/')),
        'phone_c'     => array('phone'     => array(array(10,11,14))),
    );

related functionality in orm/extension.php:


    public function check()
    {
        $valid = parent::check();
        if ($valid) $this->_validated = true;
        return $valid;
    }

    public function save()
    {
        if (count($this->_changed) && !$this->_validated) {
            if (!$this->check()) {
                throw new Validate_Exception($this->_validate);
            }
        }
        return parent::save();
    }

    public function __set($column, $val)
    {
        if ($this->_validated) $this->_validated = false;
        ....
    }

controller code in controller/mycontroller.php:


            $account->check();
            $primary_user->check();
            $pass_matches = Validate::factory($_POST)
                ->rule('password_confirm', 'not_empty')
                ->rule('password', 'matches', array('password_confirm'));
            $pass_matches->check();
            $errors = $account->errors() + $primary_user->errors() + $pass_matches->errors();

I highly recommend setting up the messages for errors, it makes the response of the errors() function much easier to work with.

Transactions

I don’t know! Hahahah. I don’t have the luxury of using a db that supports them for this project. Amazing. I’ll update here if I have time to play with it on my own. Transactions are overrated anyways.

Saving 1to1, 1toN, NtoN

Presuming you have a 1to1 relationship defined in your model, you can save a change to it like zis:


$account->primary_user->values($_POST);
$account->primary_user->save();

For 1toN/NtoN, I haven’t personally tried it but if you scroll to the bottom of the orm doc page on github, there is a blurb about using ->add() and ->remove() which may be helpful.

My catch-all for musings and experimentation in the realm of making connections and modeling.

It might be worthwhile to give a bit of landscape about the particular database I am evaluating this ORM for, we’ll call it “cupcakeshop”. It’s a decently active db, with a need to support higher activity, and not overly large in footprint. The schema is pretty disorganized and misnamed in certain areas, running on a rather old version of mysql. Being able to hide some of this disorganization behind some ORM smoke and mirrors would be awesome, particularly to minimize the impact on the code as the db gets reorganized. Being able to run on a master and multi read-only slaves is also of high interest.

  • mysql and pg connection (special guest: table name prefixing)
  • column definition
  • table name and column aliasing
  • defining 1to1, 1toN, NtoN
  • multi db and failover

Mysql and Postgres connectivity

Mysql support comes native with Kohana3, but using postgres requires the pg plugin. Being that any shift to postgres is far down the road, I didn’t explore the pg plugin too deeply. I did take a look at the source, and the code looked solid, with legit queries to retrieve column information and tables.

Mysql is pretty straight ahead, once I had copied the database.php config file from modules/orm/config to myapp/config/, it was pretty simple to point it at my localhost mysql instance. You may notice it has a table_prefix setting – there is currently a bug in this when doing joins, so avoid using it for now.


 array (
        'type'       => 'mysql',
        'connection' => array(
            'username'   => 'user',
            'hostname'   => 'localhost',
            'database'   => 'cupcakeshop',
            'password'   => 'pass',
            'persistent' => TRUE,
        ),
        'table_prefix' => '',
        'charset'      => 'utf8',
        'caching'      => FALSE,
        'profiling'    => TRUE,
    ),
);

To see if all is well, I did an extremely basic model and controller.

Model:

class Model_Shop extends ORM
{
    protected $_table_name = 'cup_myshops';
    protected $_primary_key = 'myshop_id';
}

Controller call:

$shops = ORM::factory('shop')->find_all();
print_r($shops);
exit();

Whoohoo. Not much of a problem there.

I thought I’d try out the pdo connection option, and adjusted the config to:

array (
        'type'       => 'pdo',
        'connection' => array(
            'dsn'        => 'mysql:host=localhost;dbname=cupcakeshop',
            'username'   => 'user',
            'password'   => 'pass',
            'persistent' => TRUE,
        ),

        'table_prefix' => 'cups_',
        'charset'      => 'utf8',
        'caching'      => FALSE,
        'profiling'    => TRUE,
    ),
);

And? Whoo…oops. Negative. PDO doesn’t have the introspection that a database-specific definition does, and because I hadn’t defined my fields in the model, Kohana barked at me when it tried to figure out the columns for itself:

Kohana_Exception [ 0 ]: Database method list_columns is not supported by Kohana_Database_PDO

Which leads me to…

column definition

class Model_Shop extends Model_Cupcakeshop
{
    protected $_table_name = 'cup_myshops';
    protected $_primary_key = 'myshop_id';

    protected $_table_columns = array(
        'myshop_id' => array('type' => 'int', 'is_nullable' => false),
        'shop_name' => array('type' => 'string', 'is_nullable' => false),
        'phone'     => array('type' => 'string', 'is_nullable' => true),
    );
}

A few notes here:

1) Kohana2 defines table_columns as a simple array of column names, ko3 is an assoc array of field=>array(info) – don’t confuse the two.

2) pre ko3.0.3 there is a bug in $_table_columns definition, so if you are seeing errors relating to using an array as a string or some such nonsense, check your kohana version.

3) I thought defining $_table_columns might restrict the model to only managing and retrieving those fields. That’s not entirely true. When playing about with the object itself you’ll only have access to the columns you define, but when you read from the db, it will pull all fields over before populating the object. I found this more than a little frustrating, I may have a very real reason to exclude a column, and if I don’t mention it in the model, I don’t want it pulled. A solution is to override the _load_result() call, and sadly not use the parent version at all. I’ll get into that sort of thing on the post about reading, because I had another reason I wanted to override _load_result().

table name and column aliasing

The short story: ORM doesn’t do support it. Jelly plans to support this, but isn’t to a 1.0 release yet. I did just see a forum post about how Jelly is up to 0.9.5 – almost there! Still, I wondered how difficult it would be to do something simple to take care of my needs on my own, as I had some serious schema disorganization to try and cover up. I shifted to my own definition of fields that was modified on construct, and overrode __get() and __set(), comme ca:

class Model_Shop extends Model_Myorm
{
    protected $_table_name = 'cup_users_table';
    protected $_primary_key = 'strange_user_id';

    protected $_fields = array(
        'id'    => array('column_name' => 'strange_user_id'),
        'name'  => array('column_name' => 'shop_name'),
        'phone' => array(),
    );
}
class Model_Myorm extends ORM
{
    protected $_table_columns = array();

    public function __construct()
    {
        if (count($this->_fields)) {
            $default_col_info = array('type' => 'string','is_nullable' => false);

            foreach($this->_fields as $col=>$info) {
                $info = array_merge($default_col_info, $info);

                if (isset($info['column_name'])) {
                    $this->_ignored_columns[] = $col;
                    $col = $info['column_name'];
                }

                $this->_table_columns[$col] = $info;
            }
        }

        parent::__construct();
    }

    public function translate_col($col)
    {
        if (isset($this->_fields[$col]['column_name'])) {
            return $this->_fields[$col]['column_name'];
        }
        return $col;
    }

    public function __get($column)
    {
        return parent::__get($this->translate_col($column));
    }

    public function __set($column, $val)
    {
        // to use validation on an aliased column, you do still need to set it
        parent::__set($column, $val);

        return parent::__set($this->translate_col($column), $val);
    }
}

Now your controller should be able to do things like this…

$shops = ORM::factory('shop')->find_all();
foreach($shops as $shop) {
    print $shop->id . " " . $shop->name;
}

$shop = ORM::factory('shop')->find($id);
$shop->name = 'Yummy.';
$shop->save();
exit();

This will go only so far though. When referencing a column in other functions like where() or order_by(), you’ll have to use the original column name (unless you override those as well… essentially you’d have to override several functions to gain a tenuous hold on aliasing). Aliasing is important enough to me to push forward, otherwise I would have just given up on aliasing until Jelly stabilizes.

defining 1to1, 1toN, NtoN

The ORM doc page does do well at going through relationships, so check that out for further reading. It answered all of my questions anyways. The only note I’d make to you is that, while you can eager load a 1to1 by using the ->with() method, you can’t eager load 1toN or NtoN relationships (lazy loading is fine though).

multi db and failover

I wasn’t planning to get too wild here. I wanted to be able to define a master and multiple read-only slaves in the database conf for an environment (like dev, qa, prod), and see if I could get the code to use a randomly selected slave for reads and a master on any write operation. Obviously this shouldn’t be the extent of the failover logic or you’ll be crashing and burning pretty quick, but if I can do that much, I expect I could deal with going the whole 9 yards later on.

Now being that I want to choose a random slave once per request (and not per model) I needed to either put the logic somewhere global or make the result a global var. I opted for global var. Bite me.

Tell bootstrap what environ you are in:

// DEV, QA, PROD - or anything defined in database config: _{ENVIRON}_master
define('DB_ENVIRON', 'DEV');

config/database.php:

return array
(
    '_DEV_master' => array
    (
    ... usual connection info
    ),
    '_DEV_slave1' => array
    (
    ... usual conn info ...
    ),
    '_DEV_slave2' => array
    (
    ... usual conn info ...
    ),
)

orm/extension.php:

<?php defined('SYSPATH') or die('No direct script access.');

class ORM_Extension extends ORM
{
    protected $_db = '';

    public function __construct()
    {
        if (!$this->_db) {
            $this->set_db();
        }

        ...
    }

    public function set_db($type = 'slave')
    {
        if (!isset($GLOBALS['_app_dbs'])) {
            $this->find_dbs();
        }

        if (!isset($GLOBALS['_app_dbs'][DB_ENVIRON])) {
             throw new Kohana_Exception('Invalid db environ :db_env no dbs in config match bootstrap environ', array(':db_env' => DB_ENVIRON));
        }

        $dbs = $GLOBALS['_app_dbs'][DB_ENVIRON];

        // don't do all this work if we are already set to the right db
        if ($type == 'master' && $this->_db == $dbs['master']) return;
        if ($type == 'slave' && $this->_db == $dbs['default_slave']) return;

        if ($type == 'master' || !count($dbs['slaves'])) { // or if a write occurred in the last __ minutes
            Database::instance($dbs['master'])->connect();
            $this->_db = $dbs['master'];
            return;
        }

        // connect to the best slave, likely the default already chosen for this request
        $slave = $dbs['default_slave'];

        $connected = false;
        $failed = array();
        while (!$connected) {
            try {
                Database::instance($slave)->connect();
                $connected = true;
            }
            catch (ErrorException $e)
            {
                $failed[] = $slave;
                $untried = array_merge(array_diff($dbs['slaves'], $failed));

                if (!count($untried)) {
                    // no slaves are working, set default to master so other models don't repeat this effort
                    $GLOBALS['_app_dbs'][DB_ENVIRON]['default_slave'] = $dbs['master'];
                    $this->set_db('master');
                    return;
                }

                $connected = false;
                $slave = $untried[time() % count($untried)];
            }
        }

        $this->_db = $slave;

        // reset the default to the valid slave to avoid other models thrashing through invalid dbs
        if ($slave != $dbs['default_slave']) {
            $GLOBALS['_app_dbs'][DB_ENVIRON]['default_slave'] = $slave;
        }
    }

    public function find_dbs()
    {
        $dbs = array_keys((array)Kohana::config('database'));
        foreach($dbs as $db) {
            if ($db[0] == '_') {
                list ($env, $type) = explode('_', substr($db,1));
                ($type == 'master') ?
                    $GLOBALS['_app_dbs'][$env]['master'] = $db :
                    $GLOBALS['_app_dbs'][$env]['slaves'][] = $db;
            }
        }

        if (!isset($GLOBALS['_app_dbs'][DB_ENVIRON]['slaves'])) {
            $GLOBALS['_app_dbs'][DB_ENVIRON]['slaves'] = array();
            return;
        }

        // choose a default slave for this request
        $def_slave = time() % count($GLOBALS['_app_dbs'][DB_ENVIRON]['slaves']);
        $GLOBALS['_app_dbs'][DB_ENVIRON]['default_slave'] = $GLOBALS['_app_dbs'][DB_ENVIRON]['slaves'][$def_slave];
    }

    public function save()
    {
        $this->set_db('master');

        return parent::save();
    }
    ...
}

And with that set up, I was able to screw around in my db config file, breaking certain connections, breaking all of them, and watching that it failed over properly.

profiling and debugging

Ah, this one couldn’t be simpler, and it makes me so happy. I love the profiling that comes native with ko3, and all you have to do is drop in a block for it on your main template.

<?php if (!IN_PRODUCTION) : ?>
    <div id="kohana-profiler">
    <?php echo View::factory('profiler/stats') ?>
    </div>
<?php endif ?>

I’m considering having a special user type that, even on production, will enable profiling. Love it. Screen shot:

a spliced image of what profile shows me on one of my test pages - execution times, data transfers, every query my page executed with arguments in place. I love you.

Framework Shopping

February 27, 2010

A recent project prompted a review of the current app and it’s ecosystem to find out if it had some mileage left in it or not. In my particular case, the answer was: or not. Deciding if you need to rebuild is a complicated question, depending on how big your app is, how systemic and serious the issues are and how well the current build team works together. If you are running on a prototype, the answer is an almost guaranteed yes, as the goal of a prototype rarely includes organization, scalability and security. Such non-sexy topics can be dealt with later, right!? Absolutely right. In fact, you will deal with them later, but not until you’ve been bitten hard enough by them. Luckily, the bites from this app haven’t been fatal, so now it’s time to deal with them before they are.

Fortunately, dealing with non-sexy boilerplate is a solid framework’s idea of a good time. And also fortunately, they are all ze rage right now in the LAMP world, options are plentiful (with more forking off and popping up every day) so you are likely to find a good one without needing to roll your own (hallelujah). So let’s talk about framework shopping. Which is like Christmas shopping, only you can’t afford to regift and start over if it doesn’t work out.

I had a basic set of criteria to whittle the list of frameworks down quickly to a few good candidates, followed by a bit of experimentation to narrow it further, and then finally running through the needs of the app to see how the framework handles them. If this isn’t a personal project and you are under a deadline, it’s important to keep in mind that you are looking for something solid that you won’t hate working with. In other words, don’t obsess. Make a decision and move on.

Round 1

For the basic criteria, I considered basics along with a few app-specific items.

  • is it simple and light?
  • is it a lively and active community?
  • how slow is it?
  • how straightforward does it look to build a basic page?
  • how does it handle the db layer and keeping queries safe?
  • is it multi db compatible?
  • how easily could you incorporate an api?
  • how well can you do unittesting of app logic?
  • is it php5 only, with a decent license?
  • is it internationalization friendly?
  • does it have a nice logging/debugging/profiling system?

It wasn’t that I needed a framework to ace all of these, they were just aspects to look at to help narrow things down a bit. Most of this was done by searching and reading reviews, blog posts and framework documentation.

Results from round 1: symfony, yii, doophp, kohana

Round 2

Next I wanted to install these and run through some of the tutorials they have listed (if any). Again, I couldn’t spend a whole ton of time here, so don’t get ruffled if you disagree with my conclusions.

symfony

Symfony seemed to have a rep of bloat and overkill, and was also among the slower on the list. It’s documentation seemed to confirm this opinion, so it was crossed off the list early. Looks like symfony2 is in the works, but won’t be ready for serious work until end of the year. Could be one to watch.

yii

It’s a fat install compared to the others due to the i18n files, and while that seemed to reflect a significant effort towards internat, it kind of annoyed me that it wasn’t an add-on you could download separately. Anyways. I really wanted to like this one. I really thought I would, but I just didn’t care for the style of it, and honestly that’s my only beef. It just didn’t feel natural to me.

Doophp

It couldn’t be more simple, and that’s why I liked it. You’ll get running and building pages super quick, but the ORM wasn’t real helpful nor mature enough to be usable on a high traffic site. The community was helpful, though documentation seemed slight. In general it reflected the fact that it was a new kid on the block. It’s definitely one I’ll watch, but I needed something solid sooner rather than later, so it got crossed off.

kohana (v2)

I liked how it was organized and how simple it was to get moving and start playing around. Exploring further led to a feeling of “things just work” which I really enjoyed. The ORM was helpful and seemed decently efficient without the pretensions of completely replacing SQL – something that I personally find a little absurd. It was about this time that I realized there was kohana v2 and kohana v3, and v3 had some really nice features about it – leading to a surprise entry in the race.

kohana (v3)

Having come from v2, I was familiar with certain basics, however v3 is enough of a different bird that the v2 docs don’t entirely apply. Being that v3 had little documentation at this point, that was definitely a downside. I have to say – it was the downside of my v3 work. Had it been better documented, things would have gone more smoothly.

This tension between more advanced features, but worse documentation, led to a face-off between v2 and v3. I built out some basic crud for a couple of models in my app that had decent complexity. This was a tough call. V3 is definitely less documented and quirkier (made worse by an old v3.0.* version being the main download link from their site, you definitely want the latest 3.0.*), but the code is still readable enough that I could find things on my own. V3 did, however, have several nice small enhancements, along with one really large one – it had a very charming way of allowing one controller/view to make a call to another controller, or even calling a controller from CLI – essentially server-side ajax. The level of awesomeness this could allow for, from api to unit testing seemed significant enough to overrule my concerns over documentation.

Result from round 2: Kohana v3.

Round 3

The framework review gravy train doesn’t stop here, but this blog post has to.

Next post: the gloves come off and kohana3 and I battle through every task necessary to build the app. That’s not to imply I straight up rebuilt the app. This is not a task list of pages or even a list features, think of it more as a list of ingredients. Epic.

Not in Kansas Anymore

November 25, 2009

As you may or may not know, I’ve been pretty heavy into postgres for the past five years or so. I’m no pg rock-star, but I have a few tricks up my sleeve. When I was recently faced with making a plan to tame a long undisciplined mysql db, I was intrigued. The battle between pg and mysql is well known, and here was an opportunity for me to find out first hand just how easy/difficult mysql would make this for me. First things first for me in these situations – I need me something akin to pg_stat_activity. How many active connections are there, what are they doing, filtering to connections running a query that looks like ___, sorting them based on how long they are running – I think you’d agree these are important answers for an admin to be able to get quickly when a db is being a little temperamental. As such I figured all dbs probably have something like it, with maybe only modest differences. I also used to figure you could walk on clouds because they looked like cotton balls. In both cases, reality is slightly different. My irritation at mysql for this can only go so far, considering the reason I don’t have this tool is the system hadn’t been upgraded to the latest stable version. That aside, I consider this one of the basics. It’s incredible that as long as mysql has been around, this is only recently coming out. Logs and graphs do not cut it when you are stacking and need to find a culprit fast. Anyways, it seems the best that mysql 4.1.20 can muster is an unfilterable text dump of what is currently running:

SHOW FULL PROCESSLIST;

Now this is somewhat informative, I grant you. But it shows you just enough to show you that you desperately need to do more with it to actually be helpful. Please don’t whine to me about how I could run it from command line and pipe it through grep – I’m aware of this, but when things move fast it’s absurd to think I’d need to retype in my user’s password every single time I want to see what the current queries are. Judging by the interwebs it has irritated others as well, and mysql 5.1 seems to have decided this was maaaaaaybe important enough to toss it in. When mulling over an upgrade path, I came across this gem. Wow. The bugginess of the 5.1 release was pretty incredible, and the tone of the post seems to imply this is not a new situation for a mysql release to be in. Mental note to not trust a mysql version until it’s been alive for a while and been through significant testing in dev.

So back to the most immediate concern – what do I do in the near future if the system begins to crumble and I need to quickly find and kill a problematic query? Something like an answer to that question was found in a comment I ran across discussing processlist: don’t be quick to kill procs listed in processlist or risk loosing data integrity. I’m sorry what? Loose data integrity. I sort of assumed most people were going with Innodb tables these days, and looking on MyIsam the same as we look on 80s fashion – a celebration of excess which is slightly embarrassing to look back on. Well, hammer pants are back, and apparently MyIsam was never out. It’s still the default for mysql, and it’s what I’m stuck with in this particular system. Yes, there are circumstances where using it makes sense – but really? It’s the default? Wow. Lock that sh*t down and make people move through several “are you sure?” confirmations before opening up that option. Incredible.

I feel like I’ve done a bit of beating-up on mysql in this post, but I remind myself that no transition from one dbms to another is going to be without it’s bumps in the road. In looking for other things I’ve come across some utilities that beat the pants off of anything I’ve seen for postgres – Jet profiler. Oh. My. Gosh. I’d kill for that to more permanently store some of it’s information for more long-running diagnostics, but you can always use cacti/zabbix for that I suppose. There is also a super nice slow query log analyzer I came across in this article from the mysql performance blog. Very nice. Both tools have already been helpful.

A New Blog, A New Build.

September 3, 2009

It’s time to refresh the ol’ slice. I haven’t messed with it in about a year so I suppose I’m due – one year is about 10 years in the world of technology. Going from hardy to jaunty, redoing python, apache2, django and all the other the preferences that a 9 year old developer comes to have is not a task I look on with pleasure. It’s good for you though, like jumping into a lake in winter. Has it really been 9 years? I suppose it has. Nine years of building web applications – five of the most recent spent building the same app. Though if I follow the formula I mentioned at the top, my 9 years in technology has aged me 90 years. Sounds about right actually.

Back to lakes of frigid waters – the ability to jump in with minimal fuss is just one of the reasons I love my slice. I’ve worked with a few different hosting providers, I even still have a dreamhost account – but if you don’t want to pay for a dedicated and you don’t want to claw your way through a panel to get anything done (heaven forbid cpanel) you tend to be screwed. Unless you have a slice. Ah, I didn’t even finish this post by the time my slice wiped and rebuilt to jaunty. I❤ u Slicehost.