diff --git a/sdk/eventgrid/cache.go b/sdk/eventgrid/cache.go new file mode 100644 index 0000000..2be6105 --- /dev/null +++ b/sdk/eventgrid/cache.go @@ -0,0 +1,124 @@ +package eventgrid + +import ( + "sync" + "time" +) + +// CacheDefaultMaxDepth is the maximum number of Events that will +// be stored here, before they begin automatically removed. +const CacheDefaultMaxDepth uint = 100000 + +// CacheDefaultTTL is the default length of time that each event will live +// in the cache before it is aut +const CacheDefaultTTL = time.Hour * 48 + +// Cache will hold a set number of events for a short amount of time. +type Cache struct { + sync.RWMutex + maxDepth uint + ttl time.Duration + root *cacheNode +} + +// MaxDepth gets the largest number of `Event` instances that this `Cache` +// will hold before automatically deleting the least recently arriving ones. +func (c *Cache) MaxDepth() uint { + c.RLock() + defer c.RUnlock() + + return c._MaxDepth() +} + +func (c *Cache) _MaxDepth() uint { + if c.maxDepth == 0 { + return CacheDefaultMaxDepth + } + return c.maxDepth +} + +// SetMaxDepth changes the largest number of `Event` instances that this `Cache`. +// will hold. +func (c *Cache) SetMaxDepth(depth uint) { + c.Lock() + defer c.Unlock() + + c.maxDepth = depth +} + +// TTL get the amount of time each event will last before being cleared from the `Cache`. +func (c *Cache) TTL() time.Duration { + c.RLock() + defer c.RUnlock() + + return c._TTL() +} + +func (c *Cache) _TTL() time.Duration { + if c.ttl <= 0 { + return CacheDefaultTTL + } + return c.ttl +} + +// SetTTL sets the amount of time each event will last before being cleared from the `Cache`. +func (c *Cache) SetTTL(d time.Duration) { + c.Lock() + defer c.Unlock() + + c.ttl = d +} + +// Add creates an entry in the `Cache`. +func (c *Cache) Add(e Event) { + c.Lock() + defer c.Unlock() + + created := &cacheNode{ + Event: e, + next: c.root, + expiration: time.Now().Add(c._TTL()), + } + + c.root = created +} + +// Clear removes all entries from the Event Cache. +func (c *Cache) Clear() { + c.Lock() + defer c.Unlock() + + c.root = nil +} + +// List reads all of the Events in the cache at a particular moment. +func (c *Cache) List() (results []Event) { + c.RLock() + defer c.RUnlock() + + called := time.Now() + + prev := c.root + i := uint(0) + + for current := c.root; current != nil; current = current.next { + if i >= c._MaxDepth() { + current.next = nil + break + } + + if current.expiration.After(called) { + results = append(results, current.Event) + i++ + } else { + prev.next = current.next + } + } + return +} + +type cacheNode struct { + Event + next *cacheNode + expiration time.Time +} diff --git a/sdk/eventgrid/cache_test.go b/sdk/eventgrid/cache_test.go new file mode 100644 index 0000000..8641351 --- /dev/null +++ b/sdk/eventgrid/cache_test.go @@ -0,0 +1,89 @@ +package eventgrid_test + +import ( + "bytes" + "fmt" + "testing" + "time" + + "github.com/Azure/buffalo-azure/sdk/eventgrid" +) + +func ExampleCache() { + myCache := &eventgrid.Cache{} + + myCache.Add(eventgrid.Event{ + EventType: "Contoso.Buffalo.CacheProd", + }) + myCache.Add(eventgrid.Event{ + EventType: "Microsoft.Storage.BlobCreated", + }) + + fmt.Println(myCache.List()) + + myCache.Clear() + fmt.Println(myCache.List()) + // Output: + // [{ [] Microsoft.Storage.BlobCreated 0001-01-01 00:00:00 +0000 UTC } { [] Contoso.Buffalo.CacheProd 0001-01-01 00:00:00 +0000 UTC }] + // [] +} + +func ExampleCache_SetTTL() { + myCache := &eventgrid.Cache{} + myCache.SetTTL(time.Second) + + myCache.Add(eventgrid.Event{ + EventType: "Microsoft.Storage.BlobCreated", + }) + fmt.Println(len(myCache.List())) + + <-time.After(2 * time.Second) + fmt.Println(len(myCache.List())) + + // Output: + // 1 + // 0 +} + +func ExampleCache_SetMaxDepth() { + myCache := &eventgrid.Cache{} + myCache.SetMaxDepth(2) + + fmt.Println(len(myCache.List())) + myCache.Add(eventgrid.Event{}) + fmt.Println(len(myCache.List())) + myCache.Add(eventgrid.Event{}) + fmt.Println(len(myCache.List())) + myCache.Add(eventgrid.Event{}) + fmt.Println(len(myCache.List())) + + // Output: + // 0 + // 1 + // 2 + // 2 +} + +func TestCache_SetMaxDepth(t *testing.T) { + + myCache := &eventgrid.Cache{} + myCache.SetMaxDepth(2) + + output := &bytes.Buffer{} + + fmt.Fprint(output, len(myCache.List())) + myCache.Add(eventgrid.Event{}) + fmt.Fprint(output, len(myCache.List())) + myCache.Add(eventgrid.Event{}) + fmt.Fprint(output, len(myCache.List())) + myCache.Add(eventgrid.Event{}) + fmt.Fprint(output, len(myCache.List())) + fmt.Fprint(output, len(myCache.List())) + fmt.Fprint(output, len(myCache.List())) + + const want = "012222" + if got := output.String(); got != want { + t.Logf("got: %q want: %q", got, want) + t.Fail() + } +}