Class ::acs::LockfreeCache (public)
::nx::Class ::acs::LockfreeCache
Defined in packages/acs-tcl/tcl/acs-cache-procs.tcl
Lockfree caches are provided either as per-thread caches or per-request caches, sharing the property that accessing these values does not require locks. Typical applications of these caches are the per_request_cache and per_thread_cache.
- See Also:
- Testcases:
- No testcase defined.
Source code: :property {prefix} :public method get { {-key:required} var } { # # Get entry with the provided key from this cache if it # exists. In most cases, the "eval" method should be used. # # @param key cache key # @return return boolean value indicating success. # if {[info exists ${:prefix}] && [dict exists [set ${:prefix}] $key]} { :upvar $var value set value [dict get [set ${:prefix}] $key] return 1 } return 0 } :public method eval { {-key:required} {-no_cache} {-no_empty:switch false} {-from_cache_indicator} cmd } { # # Use the "prefix" to determine whether the cache is # per-thread or per-request. # # @param key key for caching, should start with package-key # and a dot to avoid name clashes # @param cmd command to be executed. # @param no_empty don't cache empty values. This flag is # deprecated, one should use the no_cache flag # instead. # @param no_cache list of returned values that should not be cached # @param from_cache_indicator variable name to indicate whether # the returned value was from cache or not # # @return return the last value set (don't use "return"). # if {[info exists from_cache_indicator]} { :upvar $from_cache_indicator from_cache } #if {![info exists ${:prefix}]} { # ns_log notice "### exists ${:prefix} ==> 0" #} else { # ns_log notice "### [list dict exists [set ${:prefix}] $key] ==> [dict exists [set ${:prefix}] $key]" #} if {![info exists ${:prefix}] || ![dict exists [set ${:prefix}] $key]} { #ns_log notice "### call cmd <$cmd>" set from_cache 0 set value [:uplevel $cmd] if {$no_empty} { ad_log warning "no_empty flag is deprecated and will be dropped in the future." lappend no_cache "" } if {[info exists no_cache] && $value in $no_cache} { #ns_log notice "### cache eval $key returns <$value> without caching" return $value } #if {$value eq "0"} { # ns_log notice "### cache eval $key returns <$value> with caching" #} dict set ${:prefix} $key $value #ns_log notice "### [list dict set ${:prefix} $key $value]" } else { set from_cache 1 set value [dict get [set ${:prefix}] $key] } #ns_log notice "### will return [list dict get ${:prefix} $key]" return $value } #:public method flush { # {-pattern *} #} { # # # # Flush a cache entry based on the pattern (which might be # # wild-card-free). # # # ::acs::clusterwide [self] flush_local -pattern $pattern #} :public method flush { {-pattern *} } { # # Flush a cache entry based on the pattern (which might be # wild-card-free). Currently, the clusterwide flushing is # omitted. # # We have the per-request cache (clusterwide operations do # not make sense for this) and per-thread caching. The # per-thread caching application have to be aware that # flushing is happening only in one thread, so clusterwide # operations will only start to make sense, when the all # threads of a server would be cleaned. # if {[info exists ${:prefix}]} { if {$pattern eq "*"} { #ns_log notice "### dict flush ${:prefix} <$pattern>" unset -nocomplain ${:prefix} } elseif {[string first "*" $pattern] != -1} { # # A real pattern with wild-card was provided. # set keys [dict keys [set ${:prefix}] $pattern] #ns_log notice "### dict flush ${:prefix} <$pattern> -> [llength $keys]" foreach key $keys { dict unset ${:prefix} $key } } elseif [dict exists [set ${:prefix}] $pattern] { # # A "pattern" without a wildcard was provided # dict unset ${:prefix} $pattern } } } :create per_request_cache -prefix ::__acs_cache { # # Lockfree cache with per-request live time of the # entries. # # The purpose of the per-request cache is to cache # computation results of a single request. The entries of # this cache are therefore very short-lived. Some values # are needed multiple times per request, and/or they # should show consistently the same value during the same # request, no matter, if concurrently, a value is changed # (e.g. permissions). # # The per-request cache uses a Tcl variable in the global # Tcl namespace, such it will be automatically reclaimed # after the request. The per-request cache uses the prefix # "::__acs_cache". # } # # Define the "per_thread_cache" # set docString { # # Lockfree cache with per-thread live time of the entries. # # The per-thread caches use namespaced variables, # which are not touched by the automatic per-request # cleanup routines of the server. So, the values # cached in one requests can be used by some later # request in the same thread. The entries are kept in # per-thread caches as long as the thread lives, there # is so far no automatic mechanism to flush these. So, # per-thread caches are typically used for values # fetched from the database, which do not change, # unless the server is restarted. # # Note: the usage of per-thread caches is only # recommended for static values, which do no change # during the life time of the server, since there is # so far no automatic measure in place to the flush # values in every thread. } if {[ns_config "ns/parameters" cachingmode "per-node"] eq "none"} { # # If caching mode is "none", let the "per_thread_cache" behave # like the "per_request_cache". # :create per_thread_cache -prefix ::__acs_cache $docString ns_log notice "cachingmode [ns_config "ns/parameters" cachingmode singlenode]" "-> per_thread_cache behaves like per-request_cache" } else { # # The per-thread cache uses namespaced Tcl variables, identified # by the prefix "::acs:cache" # :create per_thread_cache -prefix ::acs::cache $docString }XQL Not present: Generic, PostgreSQL, Oracle