Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

model caching #417

Merged
merged 1 commit into from
Jul 16, 2014
Merged

Conversation

shmax
Copy link
Contributor

@shmax shmax commented Jul 1, 2014

Adds basic model caching functionality to active record. To enable caching for a particular model, set a static variable called "cache" to true in your model subclass, and configure a cache adapter. Set a static variable called "cache_expire" to a value in seconds; otherwise your Cache configuration expire period will be used.

@koenpunt
Copy link
Collaborator

koenpunt commented Jul 1, 2014

Good, this one looks better. Can you add some tests for this as well?

protected function update_cache(){
$table = static::table();
if($table->cache_model){
Cache::set($this->cache_key(), $this, 0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expiration time should be an option and default to something other than never.

@shmax
Copy link
Contributor Author

shmax commented Jul 2, 2014

Trying to write some tests, but I'm having some trouble getting started. I discovered /test/helpers/config.php, and the only advice it gives is to run

phpunit Alltests.php --slow-tests

or

phpunit ????Test.php

But there is no file called AllTests.php in the project, and when I try to run a specific test, I get complaints about SnakeCase_PHPUnit_Framework being missing; where is the autoloader? Which directory am I supposed to be running this from? Is there a little tutorial or more detailed information somewhere, specifically for this project?

@koenpunt
Copy link
Collaborator

koenpunt commented Jul 2, 2014

Try the following:

# install phpunit using composer
composer install
# run local version of phpunit
vendor/bin/phpunit ./test

@koenpunt
Copy link
Collaborator

koenpunt commented Jul 2, 2014

Oh and if you want to use a different mysql/sqlite configuration for testing you can supply the connection strings as environment variables:

PHPAR_MYSQL=mysql://root@127.0.0.1:3306/phpar_test \
PHPAR_PGSQL=pgsql://root@127.0.0.1/phpar_test vendor/bin/phpunit ./test

@koenpunt koenpunt mentioned this pull request Jul 2, 2014
@shmax
Copy link
Contributor Author

shmax commented Jul 2, 2014

@koenpunt Thanks much, that gets me unstuck.

@shmax
Copy link
Contributor Author

shmax commented Jul 4, 2014

@koenpunt
@cvanschalkwijk
Wrote unit tests.
Now using cache expire value from config, but you can override it by putting a $cache_expire static on your model subclass.

@shmax
Copy link
Contributor Author

shmax commented Jul 8, 2014

Not to be a pest, but can I ask what the plan is for merging some of these PRs? I see that mine is only one of many that have been waiting in line for quite some time. I only ask because there are a few other things I'd like to contribute (APC Cache adapter, version bumping for table schemas, bug fixes)...

@andyleap
Copy link

andyleap commented Jul 9, 2014

It's kinda a crap shoot, as it seems like most of the people that can push are largely inactive. IMHO, you'll be better off looking through PR's for features you need and pulling them into your own fork. That's what I'd do, but I've mostly moved on from PHP.

@koenpunt
Copy link
Collaborator

koenpunt commented Jul 9, 2014

All those PR's have to be reviewed, updated, merged and tested, and that is a lot of work. And for me, I use AR in production extensively, but I'm not in need of the features proposed here on Github, so that makes that it's not a priority for me to get this merged. So from time to time I take stab at some PR's, but it's still very time consuming, also for the fact that there are more PR's submitted over time.

@shmax
Copy link
Contributor Author

shmax commented Jul 9, 2014

I'm using it in production as well (on two sites, one commercial), but that doesn't seem like the best justification to just sit on it. Like andyleap says, you can make your own fork and keep it in a state you like. Seems like a shame to just let the main branch stagnate, especially when so many of those PRs are legitimate fixes (personally, I'm waiting for the APC cache adapter, and fixes for foreign_key attributes of has_many associations, not to mention my own caching work, which has been promised on the main website for years) or valuable new features. Given that you guys are obviously overwhelmed--and who can blame you--and not really motivated to make changes, maybe it's time to grant merging privileges to a few new folks? I'm willing to help...

@jpfuentes2
Copy link
Owner

The whitespace does not seem to be consistent in this file nor is it consistent with our poorly chosen style.

* Attempt to retrieve a value from cache using a key. If the value is not found, then the closure method
* will be invoked, and the result will be stored in cache using that key.
* @param $key
* @param $closure
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing @param $expire

@andyleap
Copy link

andyleap commented Jul 9, 2014

Defaults have to be constant expressions, best bet would be to default to an invalid value, then check for that and replace with the option value in the function

@jpfuentes2
Copy link
Owner

Defaults have to be constant expressions, best bet would be to default to an invalid value, then check for that and replace with the option value in the function

Forgive me: I haven't done PHP in a very very long time :). Agreed, maybe null and if so then use $options?


return $value;
}

public static function set($key, $var, $expire=0){
if (!static::$adapter)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the defensive programming but I don't think we do this elsewhere in this class. Is it necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are similar checks already in there; flush and get, to name two. I think you want to keep it--lessens the chance of something breaking for folks who haven't configured any cache adapters.

@andyleap
Copy link

andyleap commented Jul 9, 2014

lol, wasn't meant as an attack or anything, just run into trouble with that myself

return;

$key = static::get_namespace() . $key;

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty line.

@jpfuentes2
Copy link
Owner

lol, wasn't meant as an attack or anything, just run into trouble with that myself

No worries, didn't take it as such :)

$list = static::table()->find($options);
$table = static::table();

if($table->cache_model){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd like to see the body of this conditional moved to a method for clarity of find_by_pk

@shmax
Copy link
Contributor Author

shmax commented Jul 9, 2014

Did my best to fix the whitespace issues, changed the cache_model attribute name as suggested, and changed the default expire value.

@jpfuentes2
Copy link
Owner

Thanks for the updates! This is probably annoying at this point but we do have a certain style on this project -- terrible as it may be -- I'd like to maintain consistency. Can you fix {} to be on new lines?

The whitespace is still quite wonky. What are your editor settings like?

@shmax
Copy link
Contributor Author

shmax commented Jul 10, 2014

No worries, nitpick to your heart's delight, I take no offense. I've changed the brace style. By the way, speaking of braces, I'd like to suggest a new style rule: all if statements should use braces, even ones that are only followed by a single line. So:

if ($hasPuppy)
{
    echo "Aww, want to pet the puppy";
}

instead of

if ($hasPuppy)
    echo "Aww, want to pet the puppy";

I have a very practical justification for this suggestion: it's impossible to hit a breakpoint if the braces aren't there. ( http://bugs.xdebug.org/view.php?id=895 ) The debugger was enormously helpful to me in learning your code base, but I had to keep temporarily adding braces in order to make it workable (which may explain some of the wonky spacing; I also had tabs-as-spaces set in my IDE, which I've since changed).

I know it adds an extra line, which is why I long ago adopted the habit of putting the opening brace at the ends of lines.

if (!static::$adapter)
return $closure();

if (!($value = static::$adapter->read($key)))
static::$adapter->write($key,($value = $closure()),static::$options['expire']);
static::$adapter->write($key,($value = $closure()),$expire);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the talk on this PR is all about coding style, please be consistent with the spaces between arguments, so always add a space after a , (comma)

@shmax
Copy link
Contributor Author

shmax commented Jul 11, 2014

Added space before comma. Anything else?

@koenpunt
Copy link
Collaborator

Can you please trim down the number of commits by rebasing and merging related commits

@koenpunt
Copy link
Collaborator

And I agree on the style of if statements, they are better readable as well when having braces.

@shmax
Copy link
Contributor Author

shmax commented Jul 11, 2014

I just spent the whole morning trying to squash all those commits, and as you can see I only made things worse, just like last time. Everything looks fine on my local machine, but I have no idea how to clean up all the mess you see here. Do I have to close this PR and make a fresh one, or something?

@koenpunt
Copy link
Collaborator

You'll have to force push (git push origin implement-model-caching -f) to overwrite the remote branch.

@shmax
Copy link
Contributor Author

shmax commented Jul 12, 2014

Ah, that worked! Thanks much.

@@ -864,11 +878,26 @@ private function update($validate=true)
$dirty = $this->dirty_attributes();
static::table()->update($dirty,$pk);
$this->invoke_callback('after_update',false);
}
$this->update_cache();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indenting here is still using spaces.

@shmax
Copy link
Contributor Author

shmax commented Jul 12, 2014

Fixed tabs, added newline at end of file.

@koenpunt
Copy link
Collaborator

I meant all files, so lib/Model.php, lib/Table.php, test/fixtures/publishers.csv and test/models/Publisher.php as well.

You can see which files do not have a newline by this icon:
screen shot 2014-07-13 at 16 21 25

@shmax
Copy link
Contributor Author

shmax commented Jul 13, 2014

Newlines restored.

@@ -21,7 +21,7 @@ public function tear_down()

private function cache_get()
{
return Cache::get("1337", function() { return "abcd"; });
return Cache::get("1337", function() { return "abcd"; },Cache::$options['expire']);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to pass the expire time to cache now? Then this could break current implementations. If not, please leave the change out here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, not necessary. Just being explicit, for clarity. Removed.

@shmax
Copy link
Contributor Author

shmax commented Jul 14, 2014

Removed semicolon.

@jpfuentes2
Copy link
Owner

Thanks for putting up with us @shmax. I'm going to take another look today and concern myself more with the logic this time.

@shmax
Copy link
Contributor Author

shmax commented Jul 14, 2014

@jpfuentes2 No sweat, take all the time you need. I've already merged the code into my own fork (and have been using it quite happily on prod for weeks), so I'm not blocked, or anything.

@koenpunt
Copy link
Collaborator

@jpfuentes2 @shmax same for me

jpfuentes2 added a commit that referenced this pull request Jul 16, 2014
@jpfuentes2 jpfuentes2 merged commit e3f2655 into jpfuentes2:master Jul 16, 2014
@cvanschalkwijk
Copy link
Collaborator

@shmax I like this a lot, thanks!

@jpfuentes2
Copy link
Owner

Thanks @shmax.

@@ -367,7 +400,7 @@ private function get_meta_data()

$table_name = $this->get_fully_qualified_table_name($quote_name);
$conn = $this->conn;
$this->columns = Cache::get("get_meta_data-$table_name", function() use ($conn, $table_name) { return $conn->columns($table_name); });
$this->columns = Cache::get("get_meta_data-$table_name", function() use ($conn, $table_name) { return $conn->columns($table_name); }, Cache::$options['expire']);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After updating AR using Composer I'm getting not existing index "expire" notice. I fixed it by calling ActiveRecord\Cache::initialize(null); in ActiveRecord\Config::initialize callback. However this feature should not break existing installations which it currently does (with a strict error reporting mode).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fixed in #421 (merged)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it. Good work @koenpunt!

@filer , can you pull down latest?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick fix, guys, sorry 'bout that. I was always vaguely disturbed by the idea of a public static in Cache depending on 'initialize' being fired to be valid. What do you think about the idea of initializing it at declaration, instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants