Skip to content

Instantly share code, notes, and snippets.

@dineshappavoo
Last active April 17, 2016 05:02
Show Gist options
  • Save dineshappavoo/a07fe5d8d74e9d182c7882853e944240 to your computer and use it in GitHub Desktop.
Save dineshappavoo/a07fe5d8d74e9d182c7882853e944240 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"log"
"strconv"
)
// CODE MAY NOT COMPILE WITHOUT REDIS API IMPLEMENTATION
// ALGORITHM:
// [Ref: http://meta.stackexchange.com/questions/36728/how-are-the-number-of-views-in-a-question-calculated ]
// There is some sort of a throttling mechanism in action.
// It saves the information about a question view per visitor like in pairs:
// for anonymous users, it is IP + postID.
// for authenticated users it is UserID + postID.
// This information is saved in an expiring cache entry for about 15 minutes.
// If a subsequent hit sees the entry is still there it discards the new hit.
// If it is already gone it allows for a new record.
// Every time a new hit is registered, it is also added to a memory buffer in
// addition to the expiring cache entry. The buffer itself also expires after a
// few minutes or after it is filled up to a certain size, whichever happens first.
// When it expires, everything it has accumulated is written into the database in bulk.
// They call it a "buffered write scheme". I like the term. Basically the buffer
// entries are grouped per post and then just added to the sum of the posts
// views, no particular table to store every visit details (too much to store), like:
// UPDATE posts
// SET views = views + @NewViews
// WHERE unique_id = 36278
// And the same for every question which has any views registered in the buffer.
// To optimize and minimize the database access you send the entire data for multiple
// questions to your update query in one run. You can format the data as XML, join to
// it inside the query and perform the update in one statement.
var (
viewCountBuffer = make(map[string]int64)
)
const (
KEY_EXPIRE_TIME = 30 * 60 // 30 minutes
INMEMORY_MAX_BUFFER_SIZE = 1000 // used to maintain the buffer size
CACHE_DEFAULT_VALUE = true // cache default value.
)
func GetInMemoryPageView(identifier string, postID string) (int64, error) {
key := identifier + postID
var cacheValue bool
var viewCount int64
value, err := rediskeyvaluestore.GetValue(key)
if err != nil {
log.Printf("GetPageView() :: Error occured in getting value for key %s ", err.Error())
cacheValue = false
}else {
cacheValue, err = strconv.ParseBool(string(value))
if err != nil {
fmt.Printf("Error occured in converting cache value to bool ", err.Error())
if val, ok := viewCountBuffer[key]; ok {
viewCount = val
} else {
viewCount = 1
}
return viewCount, err
}
}
if ( cacheValue == true && err == nil) {
// key is available in cache.
// update the view in the memory buffer if needed
if val, ok := viewCountBuffer[key]; ok {
viewCount = val
} else {
viewCount = 0
}
// update the TTL
_ , err := rediskeyvaluestore.AddTTL(key, KEY_EXPIRE_TIME)
if err != nil {
log.Printf("GetPageView() :: Error occured in adding TTL value for key %s with error %s", key, err.Error())
return viewCount, err
}
} else {
// key is not available in cache.
// update the view in the memory buffer
if val, ok := viewCountBuffer[key]; ok {
viewCountBuffer[key] = val + 1
viewCount = val + 1
} else {
viewCountBuffer[key] = 1
viewCount = 1
}
// set the value for key
_, err := rediskeyvaluestore.AddKey(key, CACHE_DEFAULT_VALUE)
if err != nil {
log.Printf("GetPageView() :: Error occured in adding key to redis store for key %s with error %s", key, err.Error())
return viewCount, err
}
// set TTL for the key
_, err = rediskeyvaluestore.AddTTL(key, KEY_EXPIRE_TIME)
if err != nil {
log.Printf("GetPageView() :: Error occured in adding TTL value for key %s with error %s", key, err.Error())
return viewCount, err
}
}
if len(viewCountBuffer) >= INMEMORY_MAX_BUFFER_SIZE {
// update the database with the buffer and
// clear the buffer
}
return viewCount, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment