private void sweepCacheRec(DateTime now, CacheRec rec) { var age = 0; var cd = rec.m_CreateDate; if (cd.Ticks != 0) { age = (int)(now - cd).TotalSeconds; rec.m_AgeSec = age; // the Age field is needed for efficency not to compute dates on Table.Get() } else { rec.m_CreateDate = now; return; } var rma = rec.m_MaxAgeSec; var axp = rec.m_AbsoluteExpirationUTC; if ( (age > (rma > 0 ? rma : this.m_MaxAgeSec)) || (axp.HasValue && now > axp.Value) ) { this.Remove(rec.Key); //delete expired Interlocked.Increment(ref stat_SweepRemoveCount); } }
/// <summary> /// Puts a key-identified item into this table. /// If item with such key is already in this table then replaces it and returns false, returns true otherwise /// </summary> /// <param name="key">Item's unique key</param> /// <param name="value">Item</param> /// <param name="rec">Returns new or existing CacheRec</param> /// <param name="maxAgeSec">For how long will the item exist in cache before it gets swept out. Pass 0 to use table-level setting (default)</param> /// <param name="priority">Items priority relative to others in the table used during collision resolution, 0 = default</param> /// <param name="absoluteExpirationUTC">Sets absolute UTC time stamp when item should be swept out of cache, null is default</param> public bool Put(ulong key, object value, out CacheRec rec, int maxAgeSec = 0, int priority = 0, DateTime?absoluteExpirationUTC = null) { Interlocked.Increment(ref stat_PutCount); return(_Put(key, value, out rec, maxAgeSec, priority, absoluteExpirationUTC)); }
private bool _Put(ulong key, object value, out CacheRec rec, int maxAgeSec, int priority, DateTime?absoluteExpirationUTC) { if (value == null) { throw new NFXException(StringConsts.ARGUMENT_ERROR + "Cache.TablePut(item=null)"); } var idx = getBucketIndex(key); var lck = m_Locks[getLockIndex(idx)]; lock (lck) { var bucketed = m_Buckets[idx]; if (bucketed == null) { rec = new CacheRec(key, value, maxAgeSec, priority, absoluteExpirationUTC); m_Buckets[idx] = rec; Interlocked.Increment(ref m_Count); Interlocked.Increment(ref stat_PutInsertCount); return(true); } else { if (bucketed is Page) { var page = (Page)bucketed; lock (page) { var pidx = getPageIndex(key); var existing = page.m_Records[pidx]; if (existing != null) { if (existing.Key == key) //reuse the CacheRec instance { existing.ReuseCTOR(value, maxAgeSec, priority, absoluteExpirationUTC); Interlocked.Increment(ref stat_PutReplaceCount); rec = existing; return(false); } if (existing.m_Priority <= priority) { rec = new CacheRec(key, value, maxAgeSec, priority, absoluteExpirationUTC); page.m_Records[pidx] = rec; Interlocked.Increment(ref stat_PutCollisionCount); return(false); } rec = existing; Interlocked.Increment(ref stat_PutPriorityPreventedCollisionCount); return(false); } rec = new CacheRec(key, value, maxAgeSec, priority, absoluteExpirationUTC); page.m_Records[pidx] = rec; Interlocked.Increment(ref stat_PutInsertCount); } Interlocked.Increment(ref m_Count); return(true); } else { var existing = (CacheRec)bucketed; if (existing.Key == key) { existing.ReuseCTOR(value, maxAgeSec, priority, absoluteExpirationUTC); rec = existing; Interlocked.Increment(ref stat_PutReplaceCount); return(false); } else { //1st collision var pidx = getPageIndex(existing.Key); var npidx = getPageIndex(key); if (npidx == pidx) //collision { if (existing.m_Priority <= priority) { rec = new CacheRec(key, value, maxAgeSec, priority, absoluteExpirationUTC); m_Buckets[idx] = rec; Interlocked.Increment(ref stat_PutCollisionCount); return(false); } rec = existing; Interlocked.Increment(ref stat_PutPriorityPreventedCollisionCount); return(false); } // turn CacheRec into a Page var page = new Page(m_RecPerPage); Interlocked.Increment(ref stat_PutPageCreateCount); Interlocked.Increment(ref m_PageCount); page.m_Records[pidx] = existing; rec = new CacheRec(key, value, maxAgeSec, priority, absoluteExpirationUTC); page.m_Records[npidx] = rec; Thread.MemoryBarrier(); m_Buckets[idx] = page; //assign page to backet AFTER page init above Interlocked.Increment(ref m_Count); Interlocked.Increment(ref stat_PutInsertCount); return(true); } } } } //lock }
internal Page(int recPerPage) { m_Records = new CacheRec[recPerPage]; }