save counts in separte bucket

This commit is contained in:
Umputun
2018-02-18 21:47:30 -06:00
parent ba20003585
commit 2ddb426ec6
+42 -6
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"log"
"strconv"
"strings"
"time"
@@ -30,6 +31,7 @@ const (
lastBucketName = "last"
userBucketName = "users"
blocksBucketName = "block"
countsBucketName = "counts"
// limits
lastLimit = 1000
@@ -55,7 +57,7 @@ func NewBoltDB(sites ...BoltSite) (*BoltDB, error) {
// make top-level buckets
err = db.Update(func(tx *bolt.Tx) error {
topBuckets := []string{postsBucketName, lastBucketName, userBucketName, blocksBucketName}
topBuckets := []string{postsBucketName, lastBucketName, userBucketName, blocksBucketName, countsBucketName}
for _, bktName := range topBuckets {
if _, e := tx.CreateBucketIfNotExists([]byte(bktName)); e != nil {
return errors.Wrapf(err, "failed to create top level bucket %s", bktName)
@@ -130,6 +132,11 @@ func (b *BoltDB) Create(comment Comment) (commentID string, err error) {
if e = userIDBkt.Put([]byte(comment.Timestamp.Format(time.RFC3339Nano)), []byte(rv.value)); e != nil {
return errors.Wrapf(e, "failed to put user comment %s for %s", comment.ID, comment.User.ID)
}
if _, e = b.count(tx, comment.Locator.URL, 1); e != nil {
return errors.Wrapf(e, "failed to increment count for %s", comment.Locator)
}
return nil
})
@@ -169,6 +176,10 @@ func (b *BoltDB) Delete(locator Locator, commentID string) error {
return errors.Wrapf(err, "can't delete key %s from bucket %s", commentID, lastBucketName)
}
if _, e = b.count(tx, comment.Locator.URL, -1); e != nil {
return errors.Wrapf(e, "failed to decrement count for %s", comment.Locator)
}
return nil
})
}
@@ -254,11 +265,11 @@ func (b *BoltDB) Count(locator Locator) (count int, err error) {
}
err = bdb.View(func(tx *bolt.Tx) error {
bucket, e := b.getPostBucket(tx, locator.URL)
var e error
count, err = b.count(tx, locator.URL, 0)
if e != nil {
return e
}
count = bucket.Stats().KeyN
return nil
})
@@ -328,7 +339,7 @@ func (b *BoltDB) Blocked(siteID string) (users []BlockedUser, err error) {
return users, err
}
// List returns list of buckets, which is list of all commented posts
// List returns list of all commented posts with counters
func (b BoltDB) List(siteID string) (list []PostInfo, err error) {
bdb, err := b.db(siteID)
@@ -340,11 +351,11 @@ func (b BoltDB) List(siteID string) (list []PostInfo, err error) {
postsBkt := tx.Bucket([]byte(postsBucketName))
return postsBkt.ForEach(func(name []byte, _ []byte) error {
postURL := string(name)
bkt, e := b.getPostBucket(tx, postURL)
count, e := b.count(tx, postURL, 0)
if e != nil {
return e
}
list = append(list, PostInfo{URL: postURL, Count: bkt.Stats().KeyN})
list = append(list, PostInfo{URL: postURL, Count: count})
return nil
})
})
@@ -493,6 +504,31 @@ func (b *BoltDB) load(bkt *bolt.Bucket, key []byte) (comment Comment, err error)
return comment, nil
}
// count adds val to counts key postURL. val can be negative to substruct. if val 0 can be used as accessor
// it uses seprate counts bucket because boltdb Stat call is very slow
func (b *BoltDB) count(tx *bolt.Tx, postURL string, val int) (int, error) {
btoi := func(v []byte) int {
res, _ := strconv.Atoi(string(v))
return res
}
itob := func(v int) []byte {
return []byte(strconv.Itoa(v))
}
countBkt := tx.Bucket([]byte(countsBucketName))
countVal := countBkt.Get([]byte(postURL))
if countVal == nil {
countVal = itob(0)
}
if val == 0 {
return btoi(countVal), nil
}
newVal := itob(btoi(countVal) + val)
return btoi(newVal), countBkt.Put([]byte(postURL), newVal)
}
func (b *BoltDB) db(siteID string) (*bolt.DB, error) {
if res, ok := b.dbs[siteID]; ok {
return res, nil