Class ::acs::LockfreeCache (public)

 ::nx::Class ::acs::LockfreeCache[i]

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
[ hide source ] | [ make this the default ]
Show another procedure: