Documentation |
Documentation -> Tutorials -> Memory CachingThis page has been visited 109365 times. Table of Content (hide) 1. OpenSIPS MemCacheThe Memory Caching support from OpenSIPS wants to provide a way of caching at runtime different kind of data. These data will globally available (anywhere in the routing script) and shared between all OpenSIPS processes. The idea is to be able to store (cache) custom values for later usage. The main purpose of this MemCache support is to reduce the number of DB queries by caching data that does not need constant update from DB. DB queries are known to be one of bottle necks in the current design (see design considerations) and MemCache support will help in avoiding unnecessary DB hits. 2. OpenSIPS MemCache DesignThe Memory Caching support in OpenSIPS has a flexible and expendable design. The idea is to have a simple and transparent way of operating (in the same time) with multiple implementations / forms of memory caching. A memory caching system can be via the local shared memory (see Local Cache module) or via the System V shared memory (mem cache shared across independent applications) or via the MemCache server. The OpenSIPS core offers an API for operating with any memory caching system from the script. This API is composed out of three functions that allow the basic operations with a memory cache:
The MemCache design from OpenSIPS allow to set a lifetime (timeout) to an attribute when inserting it in the cache. This provides support for auto-removal of attributes (for re-fetching or clean-up purposes) from the cache. The implementations for the memory caching support are provided by the OpenSIPS modules. Right now there is a single implementation (see Local Cache module) that caches the data into the shared memory of OpenSIPS. In the future release, more implementations are planned to be added. 3. MemCache usage - Password caching for DB authenticationThe best way of describing the MemCache way of usage is to provide a real example - how to use memcache support for reducing the DB queries due user authentication. 3.1 The idea - how toThe idea is, after performing a DB authentication (authentication by fetching the password from DB) to store the password in memcache; the password is stored with a certain lifetime to ensure that from time to time the password is read from DB again. When a new authentication is needed, first we check if the password is available in memcache (previously stored and not yet expired); if so, we use the value from there to perform authentication without a DB hit. As we have to store in the same time the passwords of more than one users, we need to use different names for the attributes -> the attribute name will contain the user name, something like "passwd_username". 3.2 The logic - diagram
3.3 ScriptCurrent format of authentication script for REGISTER is: if (!www_authorize("", "subscriber")) { www_challenge("", "0"); exit; }; By adding memcache support, the script looks like: .... loadmodule "modules/db_mysql/db_mysql.so" loadmodule "modules/auth/auth.so" loadmodule "modules/auth_db/auth_db.so" loadmodule "modules/localcache/localcache.so" .... modparam("auth","username_spec","$avp(i:54)") modparam("auth","password_spec","$avp(i:55)") modparam("auth","calculate_ha1",1) modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") modparam("auth_db", "db_url","mysql://opensips:opensipsrw@localhost/opensips_1_2") modparam("auth_db", "load_credentials", "$avp(i:55)=password") .... .... route[x]{ ..... # do we have the password cached ? if(cache_fetch("local","passwd_$tu",$avp(i:55))) { $avp(i:54) = $tU; xlog("SCRIPT: stored password is $avp(i:55)\n"); # perform auth from variables # $avp(i:54) contains the username # $avp(i:55) contains the password if (!pv_www_authorize("")) { # authentication failed -> do challenge www_challenge("", "0"); exit; }; } else { # perform DB authentication -> # password will be loaded from DB automatically if (!www_authorize("", "subscriber")) { # authentication failed -> do challenge www_challenge("", "0"); exit; }; # after DB authentication, the password is available # in $avp(i:55) because of the "load_credentials" # module parameter. xlog("SCRIPT: storing password <$avp(i:55)>\n"); # use a 20 minutes lifetime for the password; # after that, it will erased from cache and we do # db authentication again (refresh the passwd from DB) cache_store("local","passwd_$tu","$avp(i:55)",1200); } .... } References:
3.4 ImprovementsYou can easily add 2 improvements to the previous script. Force password re-fetchingWhen PV authentication (based on cached password) fails because of invalid password (see the error codes of the pv_www_authorize() function). So, if the function returns "-2" (invalid password) it may mean that the password may changed (in DB), so you need to re-fresh. In such a case, you can simple remove the cached password and try to do DB authentication (that will use the DB password). Of course, after such a re-load, store into the cache the new value. WWW and PROXY authenticationYou can use the same cached password for both WWW (REGISTER) and PROXY (INVITE, MESSAGE, etc) authentication. The logic is the same in both blocks (WWW and PROXY) and you can share the same cached password for both blocks. This will dramatically reduce the number of DB queries. NOTE: for WWW authentication the authentication name is extracted from TO hedear, but for PROXY authentication the auth name is extracted from FROM header, so when operating with the cache, build the attribute name as "passwd_$tU" for WWW auth and as "passwd_$fU" for PROXY auth. 3.5 Performance estimationAssuming a 30 minute registration period and placing a call each 20 minutes, without any caching, there will be 5 authentication queries to DB per hour per user. This means, for 1000 users, per day : 1000 * 3 * 24 = 72000 queries per day for auth. If you set a 2 hours lifetime for cached data, you have one query per user at each 2 hours. So, for the same 1000 users, per day, you have : 1000 * 0.5 * 24 = 12000 queries per day for auth So, this gives you a reduction to with 83% (to 17%) of the numbers of queries! |