Provide a function to determine a server field value, then simply request the value with an age limit. If there is no stored value or the stored value is older than the age limit, it is automatically recalculated, stored for the next call and returned.
Applies to MarkLogic versions 7+
Create and deploy a library module with the following code.
xquery version "1.0-ml"; module namespace sf = "https://marklogic.com/lazy-load-server-field"; declare variable $trace := "lazy-load-server-field"; declare option xdmp:mapping "false"; (: Get a server field value. Recalculate if older than $age-limit. :) declare function sf:get-value( $name as xs:string, (: the name of the server field :) $age-limit as xs:dayTimeDuration?, (: the age limit of the value (empty sequence to ignore age limit) :) $f as xdmp:function (: a 0-arity function to get the server field value if necessary :) ) as item()* (: the value in the server field :) { xdmp:trace($trace, "Getting server field " || $name), if (fn:function-arity($f) gt 0) then fn:error(xs:QName("FUNCTIONNOTZEROARITY"), "Server field value function must be 0-arity") else let $sf-timestamp-and-value := xdmp:get-server-field($name) let $timestamp := $sf-timestamp-and-value[1] let $expired := fn:empty($timestamp) or (fn:exists($age-limit) and fn:current-dateTime() - $timestamp gt $age-limit) return if (fn:not($expired)) then (: server field is not expired, return value :) fn:subsequence($sf-timestamp-and-value, 2) else ( (: server field is expired, use function to get value and set server field :) xdmp:trace($trace, "Server field " || $name || " expired, calculating new value"), sf:set-value($name, xdmp:apply($f)) ) }; (: set a server field and add timestamp :) declare function sf:set-value( $name as xs:string, (: the name of the server field :) $value as item()* (: the value to set :) ) as item()* (: the value in the server field :) { xdmp:trace($trace, "Setting server field: " || $name), let $_ := xdmp:set-server-field( $name, (fn:current-dateTime(), $value) ) return $value };
The functionality now lives in a library module. To make use of it, import the module and create a 0-arity function that will calculate the value when needed. If you’re calculating function is not 0-arity
, you’ll need to create one that is (eg. by creating an anonymous function that calls your function with the required parameters). Here’s an example assuming that the library module is in /app/lib/lazy-load-server-field.xqy.
xquery version "1.0-ml"; import module namespace sf = "https://marklogic.com/lazy-load-server-field" at "/app/lib/lazy-load-server-field.xqy"; (: this is your existing function to calculate a value you want to store in the server field :) declare function local:calculate-refdata( $s1 as xs:string, $s2 as xs:string ) as xs:string { fn:string-join(($s1, $s2), " ") }; (: add this function to pass a 0-arity version of your function to the library :) declare function local:get-refdata( $s1 as xs:string, $s2 as xs:string ) as xs:string { sf:get-value( "refdata", xs:dayTimeDuration("PT24H"), function(){local:calculate-refdata($s1, $s2)} ) }; local:get-refdata("Hello", "world")
Server fields are specific to an app server and host. This means that in a clustered environment, you may have different values stored in server fields with the same name (on different nodes). However, you will never get back a value that is older than the age limit passed in.
Also note that server fields are not persistent, and are not retained in the event of a server restart. In that case, your server field value will be recalculated by calling the 0-arity
function you provided.
By continuing to use this website you are giving consent to cookies being used in accordance with the MarkLogic Privacy Statement.