tml-cache

TML basic cache

npm install tml-cache
8 downloads in the last week
16 downloads in the last month

tml-cache(3) -- basic age and count cache

SYNOPSIS

tml-cache basic age and count cache.

OPTIONS

SYNTAX

ENVIRONMENT

RETURN VALUES

STANDARDS

SECURITY CONSIDERATIONS

BUGS

HISTORY

AUTHOR

SEE ALSO

IMPLEMENTATION


Cache

    @purpose Age and size-based caches;
    @author Erin Phillips;
    @version 0.0.3-3;
    @history 2013-04-30 EMP [0.0.3-1] add-tml-build;
    @history 2013-04-30 EMP [0.0.3-2] remove-resource-client;
    @history 2013-04-30 EMP [0.0.3-3] remove-resource-client;

resources

    TMLR = tml-runtime [TmlRuntime]
    CORE = tml-core [Core]

locals

    now = TMLR::now

tags

    MAX_TTL = 4294967295;

code

aspect Marker(@ttlMillis#)

    @markerExpiration = add now() ttlMillis;
    @markerHitCount = 0;

method markerHit -> this

    @purpose increment the counter for the number of times the marker was hit;
    inc @markerHitCount;

method isMarkerActive # -> !

    =(le a @markerExpiration);

method isMarkerExpired # -> !

    =(gt a @markerExpiration);

method getMarkerHitCount ->

    =@markerHitCount;

type Value(@v?,ttlMillis#)

    uses Marker(ttlMillis)

method get -> ?

    =@v;

type Store(@name$,@mirrorAt#,@fullAt#,@ttlMillis#=##MAX_TTL;)

    initial state NORMAL -> MIRRORED;
    state MIRRORED -> FULL;
    state FULL -> NORMAL;
    @mirrorMap={}         @mirrorCount=0
    @currentMap={}        @currentCount=0
    @lastMissedKey=null   @lastTimestamp=now()
    @hitCount=0           @missCount=0
    @swapCount=0;

method getHitCount ->

    =@hitCount;

method getMissCount ->

    =@missCount;

method getSwapCount ->

    =@swapCount;

method getTtlMillis ->

    =@ttlMillis;

method get k$ opt cnt%CORE::RefInt -> opt v?

    = @currentMap.(k)
    if v
        if v.isMarkerActive(@lastTimestamp)
            unset @lastMissedKey
            inc @hitCount
            v.markerHit()
            if cnt cnt.set(v.getMarkerHitCount());
            v=v.get()
        else
            delete @currentMap.(k)
            dec @currentCount
            unset v
            if ami MIRRORED
                if delete @mirrorMap.(k)
                    dec @mirrorCount
                ;
            ;
        ;
    ;
    if noval v
        @lastMissedKey = k
        inc @missCount;

    @lastTimestamp = now()
    ;

method set v? opt k$=@lastMissedKey

    v = case v %Value v ? new Value(v,@ttlMillis);
    if ami NORMAL
        if gt @currentCount @mirrorAt
            iam MIRRORED;
    else-if ami MIRRORED
        @mirrorMap.(k) = v
        inc @mirrorCount
        if gt @currentCount @fullAt
            iam FULL;
    else
        inc @swapCount
        @currentMap = @mirrorMap
        @currentCount = @mirrorCount
        @mirrorMap = {}
        @mirrorCount = 0
        iam NORMAL
    ;
    inc @currentCount
    @currentMap.(k) = v
    unset @lastMissedKey
    ;

-- TESTS --;

test MARKER

vars o1=(new Marker(50000)).markerHit()
     o2=(new Marker(200)).markerHit();

test-eq : should return hitcount

    o1.getMarkerHitCount() vs 1

test-true : should be active

    o1.isMarkerActive(now())

test-async : should expire

    setTimeout(
        func
          enforce o2.isMarkerExpired(now()) 'Expire failed'
          done()
        ;,250
        );

    ;

test STORE

    vars o=new Store('TEST',15,20,100)
        hitCount=new CORE::RefInt()
        k;

test-async : should keep track of hit and miss counts

    count 1 to 101 as i
        k=con 'key' i;
        o.get(k,hitCount)
        o.set(con 'value' k)
        o.get(k,hitCount)
    ;

    enforce eq o.getMissCount() 101 'miss count failed'
    enforce eq o.getHitCount() 101  'hit count failed' 
    enforce eq o.getSwapCount() 5 'swap count failed'

    done()
    ;

    ;
npm loves you