Get Yii DB Cache id of saved record in cache table - yii

I have a query like this:
$record=books::model()->cache(10000)->find('id=:id',array(":id"=>$id));
Yii uses CDbCache to save the result in the cache table
id expire value
-- ------ ------
My question is:
How to get the id of the cache record -this id is generated by yii - in
yii cache table?

There's no public method to get the cache ID, as it incorporates a lot of variables, you could if you really wanted to but it's really dark down in the depths of Yii.
Answer based on comments:
You could always set a last cached time on your record, which you can then use to calculate the time remaining. But this won't persist over page spans, because your caching you result and it won't get read again. So your options are:
Do the caching manually, and store the time against the object to be used for calculation
Or store the time for each ID pulled in the session, and use that to calculate it.

To Answer this question. By the way the Yii code is written, you can't get the ID/Key for the Cache which the YII generates internally.
If you look at the sourcecode
https://github.com/yiisoft/yii/blob/1.1.16/framework/db/CDbCommand.php
Method queryInternal
You'll realize that the cacheKey generation is the complex thing. Moreover this cacheKey will further be Hashed by MD5
Refer: http://www.yiiframework.com/doc/api/1.1/CCache#generateUniqueKey-detail
If you are using 'cache' as part of QueryBuilder, then we are giving the power to Yii to manage this cache by providing the Expire Duration and Dependency Query.
If you want to be in command/control of the Expire Duration and want to have the ID, then only way is to manually set the CacheID as suggested by the user
Paystey as belowt:
$dbResult = Yii::app()->cache->get('YOUR-KNOWN-KEY');
if($dbResult === false){
$dbResult = YOUR-QUERY;
Yii::app()->cache->set('YOUR-KNOWN-KEY, $dbResult);
}

Related

Set expiry time for a session field

Is there a way in Rails 3 to expire a field in the session? When I searched for this I only found posts about how to expire the whole session, which I do not want to do.
In my situation I have the following:
session["END_DATE"] = #end_date
I want to tell Rails to expire the field session["END_DATE"] after a certain amount of time, which is different and a lot less than the global expiry time for the whole session.
I can always use a cookie instead of a session for that field, but it would be good to have it in the session if it is possible.
you can do something like this:
session.delete(:end_date) to delete something(end_date in your case) from your session.
Hope it helps!

Datamodel design for an application using Redis

I am new to redis and I am trying to figure out how redis can be used.
So please let me know if this is a right way to build an application.
I am building an application which has got only one data source. I am planning to run a job on nightly basis to get data into a file.
Now I have a front end application, that needs to render this data in different formats.
Example application use case
Download processed applications by a university on nightly basis.
Display how many applications got approved or rejected.
Display number of applications by state.
Let user search for an application by application id.
Instead of using postgres/mysql like relational database, I am thinking about using redis. I am planning to store data in following ways.
Application id -> Application details
State -> List of application ids
Approved -> List of application ids (By date ?)
Declined -> List of application ids (By date ?)
Is this correct way to store data into redis?
Also if someone queries for all applications in california for a certain date,
I will be able to pull application ids in one call but to get details for each application, do I need to make another request?
Word of caution:
Instead of using postgres/mysql like relational database, I am thinking about using redis.
Why? Redis is an amazing database, but don't use the right hammer for the wrong nail. Use Redis if you need real time performance at scale, but don't try make it replace an RDBMS if that's what you need.
Answer:
Fetching data efficiently from Redis to answer your queries depends on how you'll be storing it. Therefore, to determine the "correct" data model, you first need to define your queries. The data model you proposed is just a description of the data - it doesn't really say how you're planning to store it in Redis. Without more details about the queries, I would store the data as follows:
Store the application details in a Hash (e.g. app:<id>)
Store the application IDs in a per state in Set (e.g. apps:<state>)
Store the approved/rejected applications in two Sorted Sets, the id being the member and the date being the score
Also if someone queries for all applications in california for a certain date, I will be able to pull application ids in one call but to get details for each application, do I need to make another request?
Again, that depends on the data model but you can use Lua scripts to embed this logic and execute it in one call to the database.
First of all you can use a Hash to store structured Data. With Lists (ZSets) and Sets you can create indexes for an ordered or unordered access. (Depending on your requirements of course. Make a list of how you want to access your data).
It is possible to get all data as json of an index in one go with a simple redis script (example using an unordered set):
local bulkToTable = function(bulk)
local retTable = {};
for index = 1, #bulk, 2 do
local key = bulk[index];
local value = bulk[index+1];
retTable[key] = value;
end
return retTable;
end
local functionSet = redis.call("SMEMBERS", "app:functions")
local returnObj = {} ;
for index = 1, #functionSet, 1 do
returnObj[index] = bulkToTable(redis.call("HGETALL", "app:function:" .. functionSet[index]));
returnObj[index]["functionId"] = functionSet[index];
end
return cjson.encode(returnObj);
more information about redis scripts see here : http://www.redisgreen.net/blog/intro-to-lua-for-redis-programmers/

yii caching and couchbase

need some help to clarify the concept.
$sql = 'SELECT * FROM tbl_post LIMIT 20';
$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post');
$rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();
1.if the cache contains an entry indexed by the SQL statement.
2.if the dependency has not changed (the maximum update_time value is the same as when the query result was saved in the cache).
I do not understand what do the above explanation means. Especially second one with regards to maximum update_time. Please correct me if I am wrong.
There is a update_time column in tbl_post table. Whenever a row is updated, the update_time is updated too. If a post is retrieved from the cache, CDbCacheDependency will first query the database for MAX(update_time)? What is the purpose of this and how exactly does it works in keeping the cache updated?
Another question is regarding memcache. I understand that it is possible to cluster memcache servers. Say I have the below configurations.
1 memcache server in US. 1 memcache server in Europe.
My Yii website makes use of the cluster of 2 nodes. memcache will split the caching between the 2 nodes.
1.user A retrieves a post from database and cached it. assume (123,$model) in US node.
2.user B wants to retrieve the same post, from Europe. Will looking for key 123 finds the cache? Does it matters if both users are in US or Europe?
Thanks!!
After first run - DB component puts its result into cache. Also it puts there result of dependency-query (max update time in your case).
Then when your try to get data, db component executs dependency query and compare it with cached one. If dependency is unchanged (there is no new posts) it get query results from cache, in other case it execute`s query.

querying generation_time on mongo ids

John Nunemaker has a blog post with some nice tips about Mongo ObjectIds -- http://mongotips.com/b/a-few-objectid-tricks/ -- in particular I was interested in the tip about generation_time. He suggests it's not necessary to explicitly store the created_at time in mongo documents because you can always pull it from the ID, which caught my attention. Problem is I can't figure out how to generate mongo queries in mongomapper to find documents based on creation time if all I have is the id.
If I store a key :created_at as part of the document I can do a query in mongomapper to get all documents created since Dec 1st like this:
Foo.where(:created_at.gt=>Time.parse("2011-12-01"))
(which maps to:
{created_at: {"$gt"=>Thu Dec 01 06:00:00 UTC 2011}}
I can't figure out how to make the equivalent query using the ObjectId.. I imagine it'd look something like this (though obviously generation_time is a ruby function, but is there an equivalent I can use on the objectid in the context of a mongo query?)
Foo.where('$where'=>"this.id.generation_time > new Date('2011-12-01')")
{$where: "this.id.generation_time > new Date('2011-12-01')"}
One further question: if I forgo storing separate timestamps, will I lose the timestamp metadata if I dump and restore my database using mongodump? Are there recommended backup/restore techniques that preserve ObjectIds?
this is javascript code which would be run in the shell but generation time is a mongomapper method so it doesn't make sense in the code you have.
In rails you would get the id by saying something like
created_at = self.id.generation_time.in_time_zone(Time.zone)
Where self refers to an instance of Foo.
And you would query by saying
Foo.find('_id' => {'$gte' => BSON::ObjectId.from_time(created_at)}).count
Why bother though... the hassle isn't worth it, just store the time.
Regarding the backup/restore techniques, unless you are manually reading and re-inserting mongodump/restore and similar tools will preserve the object id so you have nothing to worry about there.

Are NHibernate ICriteria queries cached or put in the identity map?

Using NHibernate I usually query for single records using the Get() or Load() methods (depending on if I need a proxy or not):
SomeEntity obj = session.Get<SomeEntity>(new PrimaryKeyId(1));
Now, if I execute this statement twice, like the example below, I only see one query being executed in my unittests:
SomeEntity obj1 = session.Get<SomeEntity>(new PrimaryKeyId(1));
SomeEntity obj2 = session.Get<SomeEntity>(new PrimaryKeyId(1));
So far, so good. But I noticed some strange behaviour when getting the same object using a ICriteria query. Check out my code below: I get the first object instance. I then change the value of a property to 10 (the value in the database is 8), get another instance and finally check the values of the second object instance.
//get the first object instance.
SomeEntity obj1 = session.CreateCriteria(typeof(SomeEntity))
.Add(Restrictions.Eq("Id", new PrimaryKeyId(1)))
.UniqueResult<SomeEntity>();
//the value in the database and the property is 8 at this point. Let's set it to 10.
obj1.SomeValue = 10;
//get the second object instance.
SomeEntity obj2 = session.CreateCriteria(typeof(SomeEntity))
.Add(Restrictions.Eq("Id", new PrimaryKeyId(1)))
.UniqueResult<SomeEntity>();
//check if the values match.
Assert.AreEqual(8, obj2.SomeValue);
Now, for some reason the assert fails, because the value is 10 of obj2 even though I asked for the object with a new query. the funny thing is, there are 2 exactly the same select queries being executed according to my unit test output window. My question: why are there 2 queries being executed if the second object is fetched from the first level cache?
Am I missing something or is this a bug?
Regards, Ted
edit #1: using NHibernate v2.1.2GA
edit #2: I added some extra explanation about the 2 queries being executed to the last paragraph.
Well, having learned a lot more about NHibernate I can now answer this question myself:
The ICriteria query returns a list of objects fetched by NHibernate. NHibernate does not know which objects are returned until they are matched one by one with the object in the first level cache. If the item is already in the first level cache map the item read from the database is discarded. if it is not in the identity map, the item is put into the first level cache.
Another "a-ha!" moment: suppose you run the query for the first time while there are 5 rows in the database all rows are fetched and put into first level cache. now over time 5 more records are added to the table and you rerun the query. Now all 10 records are fetched, but NHibernate sees 5 of them are already in the cache and will only add the 5 latter records. So basically you fetched 5 records for nothing (just to match the identifiers with the object identifiers in the identity map).
Get/Load use the 1st level cache, this is why you don't see the 2nd call out the db. Queries do not use the 1st level cache. However, you can set up queries to use the 2nd level cache. See details here
UPDATE What's likely happening is the query is doing a 2 phase load. So it's getting the result set, but also checking the 1st level cache to see if any entities exist there. If they do, then it returns the cached object. See NHibernate.Loader.Loader.GetRow method.
Here is the relevant line:
//If the object is already loaded, return the loaded one
obj = session.GetEntityUsingInterceptor(key);
AFAIK, only 'Get' (and maybe Load) use the 1st level cache.
Using the Criteria API always results in a query hitting the DB, unless the 2nd level cache is enabled.
Edit: more information can be found here
I am not sure why a second query is ran, but the expected behavior of NHibernate is if you ask for the same object by ID from the same session, you get the first level cache.
In my understanding, when using a Criteria, you are basically saying to NHibernate: "I want to filter rows based on expressions".
When seen that way, NHibernate has no way of knowing if the query will always return the same filtered row(s) from the database, so it has to query it again.
Also, you can use query caching only with second-level caching, as per the documentation:
So the query cache should always be used in conjunction with the second-level cache.
From here
NHibernate is probably issuing an update between the first and second queries to protect you from a concurrency problem. As Frederik pointed out, you should always use Get to retrieve an object by its key.
I'm curious, what is the PrimaryKeyId wrapper adding?
EDIT:
However it's working (my money's still on an update before select), this behavior is by design. If you want to discard your in-memory object and load a new instance of it from the session, then Evict the original from the session first. There is also a Refresh method you could try.