Last active
April 17, 2016 05:02
-
-
Save dineshappavoo/a07fe5d8d74e9d182c7882853e944240 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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