Skip to content

Instantly share code, notes, and snippets.

@benbjohnson
Created June 13, 2014 12:04
Show Gist options
  • Select an option

  • Save benbjohnson/a3e9e35f73dae8d15c49 to your computer and use it in GitHub Desktop.

Select an option

Save benbjohnson/a3e9e35f73dae8d15c49 to your computer and use it in GitHub Desktop.

Revisions

  1. benbjohnson created this gist Jun 13, 2014.
    68 changes: 68 additions & 0 deletions store.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    package store

    import (
    "fmt"

    "github.com/boltdb/bolt"
    )

    // reap removes sessions older than a given duration.
    // This function assumes that all session data is stored in a "sessions" bucket
    // and the data is organized so the key is the session id and the value is
    // laid out as:
    //
    // -8 bytes- --n bytes--
    // timestamp + sessiondata
    //
    func reap(db *bolt.DB, duration time.Duration) error {
    // The batch size represents how many sessions we'll check at
    // a time for a given transaction. We don't want to check all the
    // sessions every time because that would lock the database for
    // too long if the sessions bucket gets too large.
    batchsz := 1000

    var prev []byte
    for {
    // Get the current timestamp.
    now := time.Now()

    // Iterate over a subset of keys at a time and delete old ones.
    err := db.Update(func(tx *bolt.Tx) error {
    c := tx.Bucket([]byte("sessions")).Cursor()

    var i int
    for k, v := c.Seek(prev); ; k, v = c.Next() {
    // If we hit the end of our sessions then exit and start over next time.
    if k == nil {
    seek = nil
    return nil
    }

    // If we have iterated over our batch size then save our place
    // and we'll start from there next time. We need to copy over
    // the bytes in "k" because it's not guarenteed to exist there
    // after the transaction is over.
    if i == batchsz {
    seek = make([]byte, len(k))
    copy(seek, k)
    return nil
    }

    // If we've made it this far then we can check if a session's
    // timestamp is older than our expiration "duration". If so
    // then we can delete the item in-place in the cursor.
    timestamp := time.Unix(int64(binary.BigEndian.Uint64(v)), 0)
    if now.Since(timestamp) > duration {
    if err := c.Delete(); err != nil {
    return fmt.Errorf("delete: %s", err)
    }
    }
    }
    })
    if err != nil {
    return err
    }

    time.Sleep(1 * time.Second)
    }
    }