mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-06 00:25:55 +00:00
Merge pull request #54 from umbel/todd
fixed issue with SetBit/ClearBit not correctly reporting bitchange
This commit is contained in:
commit
a12ec57eec
9 changed files with 279 additions and 89 deletions
|
|
@ -5,9 +5,11 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
|
|
@ -18,7 +20,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/umbel/pilosa"
|
||||
"github.com/umbel/pilosa/internal"
|
||||
)
|
||||
|
||||
// Build holds the build information passed in at compile time.
|
||||
|
|
@ -72,8 +76,10 @@ func main() {
|
|||
|
||||
// Main represents the main program execution.
|
||||
type Main struct {
|
||||
index *pilosa.Index
|
||||
ln net.Listener
|
||||
index *pilosa.Index
|
||||
ln net.Listener
|
||||
ticker *time.Ticker
|
||||
pollingSecs int
|
||||
|
||||
// Path to the configuration file.
|
||||
ConfigPath string
|
||||
|
|
@ -94,7 +100,6 @@ type Main struct {
|
|||
func NewMain() *Main {
|
||||
return &Main{
|
||||
Config: NewConfig(),
|
||||
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
|
|
@ -176,13 +181,84 @@ func (m *Main) Run(args ...string) error {
|
|||
// Serve HTTP.
|
||||
go func() { http.Serve(ln, h) }()
|
||||
|
||||
//sync up max slice if more than one node
|
||||
if len(cluster.Nodes) > 1 {
|
||||
m.ticker = time.NewTicker(time.Second * time.Duration(m.pollingSecs))
|
||||
go func() {
|
||||
for range m.ticker.C {
|
||||
oldmax:= m.index.SliceN()
|
||||
newmax:=oldmax
|
||||
for _, node := range cluster.Nodes {
|
||||
if hostname != node.Host {
|
||||
newslice,_:=checkMaxSlice(node.Host)
|
||||
if newslice>newmax{
|
||||
newmax= newslice
|
||||
}
|
||||
}
|
||||
}
|
||||
if newmax>oldmax{
|
||||
m.index.SetMax(newmax)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
fmt.Fprintf(m.Stderr, "Listening as http://%s\n", hostname)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMaxSlice(hostport string) (uint64, error) {
|
||||
|
||||
// Create HTTP request.
|
||||
req, err := http.NewRequest("GET", (&url.URL{
|
||||
Scheme: "http",
|
||||
Host: hostport,
|
||||
Path: "/slices/max",
|
||||
}).String(), nil)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Require protobuf encoding.
|
||||
req.Header.Set("Accept", "application/x-protobuf")
|
||||
req.Header.Set("Content-Type", "application/x-protobuf")
|
||||
|
||||
// Send request to remote node.
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read response into buffer.
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Check status code.
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return 0, fmt.Errorf("invalid status: code=%d, err=%s", resp.StatusCode, body)
|
||||
}
|
||||
|
||||
// Decode response object.
|
||||
pb := internal.SliceMaxResponse{}
|
||||
|
||||
if err = proto.Unmarshal(body, &pb); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return *pb.SliceMax, nil
|
||||
|
||||
}
|
||||
|
||||
// Close shuts down the process.
|
||||
func (m *Main) Close() error {
|
||||
if m.ticker != nil {
|
||||
m.ticker.Stop()
|
||||
}
|
||||
if m.ln != nil {
|
||||
m.ln.Close()
|
||||
}
|
||||
|
|
@ -200,6 +276,7 @@ func (m *Main) ParseFlags(args []string) error {
|
|||
fs.SetOutput(m.Stderr)
|
||||
fs.StringVar(&m.ConfigPath, "config", "", "config path")
|
||||
fs.StringVar(&m.CPUProfile, "cpuprofile", "", "write cpu profile to file")
|
||||
fs.IntVar(&m.pollingSecs, "pollingSecs", 60, "number of seconds to poll the cluster for maxslice")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -351,10 +351,11 @@ func (e *Executor) executeSetBit(db string, c *pql.SetBit, opt *ExecOptions) (bo
|
|||
}
|
||||
|
||||
// Forward call to remote node otherwise.
|
||||
if _, err := e.exec(node, db, &pql.Query{Root: c}, nil, opt); err != nil {
|
||||
if res, err := e.exec(node, db, &pql.Query{Root: c}, nil, opt); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
ret = res.(bool)
|
||||
}
|
||||
fmt.Println("NEED TO IMPLEMENT REMOTE SETBIT")
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
|
@ -464,7 +465,7 @@ func (e *Executor) exec(node *Node, db string, q *pql.Query, slices []uint64, op
|
|||
case *pql.Count:
|
||||
return pb.GetN(), nil
|
||||
case *pql.SetBit:
|
||||
return nil, nil
|
||||
return pb.GetChanged(), nil
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid node for remote exec: %T", q.Root))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,14 +123,29 @@ func TestExecutor_Execute_SetBit(t *testing.T) {
|
|||
defer idx.Close()
|
||||
|
||||
e := NewExecutor(idx.Index, NewCluster(1))
|
||||
if _, err := e.Execute("d", MustParse(`SetBit(id=10, frame=f, profileID=1)`), nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
f := idx.MustCreateFragmentIfNotExists("d", "f", 0)
|
||||
if n := f.Bitmap(11).Count(); n != 0 {
|
||||
t.Fatalf("unexpected bitmap count: %d", n)
|
||||
}
|
||||
|
||||
f := idx.MustCreateFragmentIfNotExists("d", "f", 0)
|
||||
if n := f.Bitmap(10).Count(); n != 1 {
|
||||
if res, err := e.Execute("d", MustParse(`SetBit(id=11, frame=f, profileID=1)`), nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if !res.(bool) {
|
||||
t.Fatalf("expected bit changed")
|
||||
}
|
||||
}
|
||||
|
||||
if n := f.Bitmap(11).Count(); n != 1 {
|
||||
t.Fatalf("unexpected bitmap count: %d", n)
|
||||
}
|
||||
if res, err := e.Execute("d", MustParse(`SetBit(id=11, frame=f, profileID=1)`), nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if res.(bool) {
|
||||
t.Fatalf("expected bit unchanged")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure a SetBitmapAttrs() query can be executed.
|
||||
|
|
|
|||
19
fragment.go
19
fragment.go
|
|
@ -329,18 +329,23 @@ func (f *Fragment) SetBit(bitmapID, profileID uint64, t *time.Time, q TimeQuantu
|
|||
|
||||
func (f *Fragment) setBit(bitmapID, profileID uint64) (changed bool, bool error) {
|
||||
// Determine the position of the bit in the storage.
|
||||
changed = false
|
||||
pos, err := f.pos(bitmapID, profileID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Write to storage.
|
||||
if err := f.storage.Add(pos); err != nil {
|
||||
|
||||
if changed, err = f.storage.Add(pos); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Update the cache.
|
||||
return f.bitmap(bitmapID).setBit(profileID), nil
|
||||
if f.bitmap(bitmapID).setBit(profileID) {
|
||||
changed = true
|
||||
}
|
||||
return changed, nil
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -368,12 +373,16 @@ func (f *Fragment) ClearBit(bitmapID, profileID uint64) (bool, error) {
|
|||
}
|
||||
|
||||
// Write to storage.
|
||||
if err := f.storage.Remove(pos); err != nil {
|
||||
changed, err := f.storage.Remove(pos)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Update the cache.
|
||||
return f.bitmap(bitmapID).clearBit(profileID), nil
|
||||
if f.bitmap(bitmapID).clearBit(profileID) {
|
||||
return true, nil
|
||||
}
|
||||
return changed, nil
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -531,7 +540,7 @@ func (f *Fragment) Import(bitmapIDs, profileIDs []uint64) error {
|
|||
}
|
||||
|
||||
// Write to storage.
|
||||
if err := f.storage.Add(pos); err != nil {
|
||||
if _, err := f.storage.Add(pos); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
handler.go
27
handler.go
|
|
@ -73,6 +73,13 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
case "/slices/max":
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
h.handleGetSliceMax(w, r)
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
case "/fragment/data":
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
|
|
@ -144,6 +151,24 @@ func (h *Handler) handlePostQuery(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Handler) handleGetSliceMax(w http.ResponseWriter, r *http.Request) error {
|
||||
|
||||
sm := h.Index.SliceN()
|
||||
if strings.Contains(r.Header.Get("Accept"), "application/x-protobuf") {
|
||||
pb := &internal.SliceMaxResponse{
|
||||
SliceMax: &sm,
|
||||
}
|
||||
if buf, err := proto.Marshal(pb); err != nil {
|
||||
return err
|
||||
} else if _, err := w.Write(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resp := map[string]uint64{"SliceMax": sm}
|
||||
return json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
// readProfiles returns a list of profile objects by id.
|
||||
func (h *Handler) readProfiles(db *DB, ids []uint64) ([]*Profile, error) {
|
||||
if db == nil {
|
||||
|
|
@ -508,6 +533,8 @@ func encodeQueryResponse(resp *QueryResponse) *internal.QueryResponse {
|
|||
pb.Pairs = encodePairs(result)
|
||||
case uint64:
|
||||
pb.N = proto.Uint64(result)
|
||||
case bool:
|
||||
pb.Changed = proto.Bool(result)
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid query result type: %T", resp.Result))
|
||||
}
|
||||
|
|
|
|||
17
index.go
17
index.go
|
|
@ -9,8 +9,9 @@ import (
|
|||
|
||||
// Index represents a container for fragments.
|
||||
type Index struct {
|
||||
mu sync.Mutex
|
||||
path string
|
||||
mu sync.Mutex
|
||||
path string
|
||||
remoteMax uint64
|
||||
|
||||
// Databases by name.
|
||||
dbs map[string]*DB
|
||||
|
|
@ -19,8 +20,9 @@ type Index struct {
|
|||
// NewIndex returns a new instance of Index.
|
||||
func NewIndex(path string) *Index {
|
||||
return &Index{
|
||||
path: path,
|
||||
dbs: make(map[string]*DB),
|
||||
path: path,
|
||||
dbs: make(map[string]*DB),
|
||||
remoteMax: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +74,7 @@ func (i *Index) SliceN() uint64 {
|
|||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
var sliceN uint64
|
||||
sliceN := i.remoteMax
|
||||
for _, db := range i.dbs {
|
||||
if n := db.SliceN(); n > sliceN {
|
||||
sliceN = n
|
||||
|
|
@ -154,3 +156,8 @@ func (i *Index) CreateFragmentIfNotExists(db, frame string, slice uint64) (*Frag
|
|||
}
|
||||
return f.CreateFragmentIfNotExists(slice)
|
||||
}
|
||||
func (i *Index) SetMax(newmax uint64) {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
i.remoteMax = newmax
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,19 +21,22 @@ It has these top-level messages:
|
|||
ImportRequest
|
||||
ImportResponse
|
||||
Cache
|
||||
SliceMaxResponse
|
||||
*/
|
||||
package internal
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
type Bitmap struct {
|
||||
Chunks []*Chunk `protobuf:"bytes,1,rep" json:"Chunks,omitempty"`
|
||||
Attrs []*Attr `protobuf:"bytes,2,rep" json:"Attrs,omitempty"`
|
||||
Chunks []*Chunk `protobuf:"bytes,1,rep,name=Chunks" json:"Chunks,omitempty"`
|
||||
Attrs []*Attr `protobuf:"bytes,2,rep,name=Attrs" json:"Attrs,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -56,8 +59,8 @@ func (m *Bitmap) GetAttrs() []*Attr {
|
|||
}
|
||||
|
||||
type Chunk struct {
|
||||
Key *uint64 `protobuf:"varint,1,req" json:"Key,omitempty"`
|
||||
Value []uint64 `protobuf:"varint,2,rep" json:"Value,omitempty"`
|
||||
Key *uint64 `protobuf:"varint,1,req,name=Key" json:"Key,omitempty"`
|
||||
Value []uint64 `protobuf:"varint,2,rep,name=Value" json:"Value,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -80,8 +83,8 @@ func (m *Chunk) GetValue() []uint64 {
|
|||
}
|
||||
|
||||
type Pair struct {
|
||||
Key *uint64 `protobuf:"varint,1,req" json:"Key,omitempty"`
|
||||
Count *uint64 `protobuf:"varint,2,req" json:"Count,omitempty"`
|
||||
Key *uint64 `protobuf:"varint,1,req,name=Key" json:"Key,omitempty"`
|
||||
Count *uint64 `protobuf:"varint,2,req,name=Count" json:"Count,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -104,8 +107,8 @@ func (m *Pair) GetCount() uint64 {
|
|||
}
|
||||
|
||||
type Bit struct {
|
||||
BitmapID *uint64 `protobuf:"varint,1,req" json:"BitmapID,omitempty"`
|
||||
ProfileID *uint64 `protobuf:"varint,2,req" json:"ProfileID,omitempty"`
|
||||
BitmapID *uint64 `protobuf:"varint,1,req,name=BitmapID" json:"BitmapID,omitempty"`
|
||||
ProfileID *uint64 `protobuf:"varint,2,req,name=ProfileID" json:"ProfileID,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -128,8 +131,8 @@ func (m *Bit) GetProfileID() uint64 {
|
|||
}
|
||||
|
||||
type Profile struct {
|
||||
ID *uint64 `protobuf:"varint,1,req" json:"ID,omitempty"`
|
||||
Attrs []*Attr `protobuf:"bytes,2,rep" json:"Attrs,omitempty"`
|
||||
ID *uint64 `protobuf:"varint,1,req,name=ID" json:"ID,omitempty"`
|
||||
Attrs []*Attr `protobuf:"bytes,2,rep,name=Attrs" json:"Attrs,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -152,10 +155,10 @@ func (m *Profile) GetAttrs() []*Attr {
|
|||
}
|
||||
|
||||
type Attr struct {
|
||||
Key *string `protobuf:"bytes,1,req" json:"Key,omitempty"`
|
||||
StringValue *string `protobuf:"bytes,2,opt" json:"StringValue,omitempty"`
|
||||
UintValue *uint64 `protobuf:"varint,3,opt" json:"UintValue,omitempty"`
|
||||
BoolValue *bool `protobuf:"varint,4,opt" json:"BoolValue,omitempty"`
|
||||
Key *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"`
|
||||
StringValue *string `protobuf:"bytes,2,opt,name=StringValue" json:"StringValue,omitempty"`
|
||||
UintValue *uint64 `protobuf:"varint,3,opt,name=UintValue" json:"UintValue,omitempty"`
|
||||
BoolValue *bool `protobuf:"varint,4,opt,name=BoolValue" json:"BoolValue,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +195,7 @@ func (m *Attr) GetBoolValue() bool {
|
|||
}
|
||||
|
||||
type AttrMap struct {
|
||||
Attrs []*Attr `protobuf:"bytes,1,rep" json:"Attrs,omitempty"`
|
||||
Attrs []*Attr `protobuf:"bytes,1,rep,name=Attrs" json:"Attrs,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -208,12 +211,12 @@ func (m *AttrMap) GetAttrs() []*Attr {
|
|||
}
|
||||
|
||||
type QueryRequest struct {
|
||||
DB *string `protobuf:"bytes,1,req" json:"DB,omitempty"`
|
||||
Query *string `protobuf:"bytes,2,req" json:"Query,omitempty"`
|
||||
Slices []uint64 `protobuf:"varint,3,rep" json:"Slices,omitempty"`
|
||||
Profiles *bool `protobuf:"varint,4,opt" json:"Profiles,omitempty"`
|
||||
Timestamp *int64 `protobuf:"varint,5,opt" json:"Timestamp,omitempty"`
|
||||
Quantum *uint32 `protobuf:"varint,6,opt" json:"Quantum,omitempty"`
|
||||
DB *string `protobuf:"bytes,1,req,name=DB" json:"DB,omitempty"`
|
||||
Query *string `protobuf:"bytes,2,req,name=Query" json:"Query,omitempty"`
|
||||
Slices []uint64 `protobuf:"varint,3,rep,name=Slices" json:"Slices,omitempty"`
|
||||
Profiles *bool `protobuf:"varint,4,opt,name=Profiles" json:"Profiles,omitempty"`
|
||||
Timestamp *int64 `protobuf:"varint,5,opt,name=Timestamp" json:"Timestamp,omitempty"`
|
||||
Quantum *uint32 `protobuf:"varint,6,opt,name=Quantum" json:"Quantum,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -264,11 +267,12 @@ func (m *QueryRequest) GetQuantum() uint32 {
|
|||
}
|
||||
|
||||
type QueryResponse struct {
|
||||
Err *string `protobuf:"bytes,1,opt" json:"Err,omitempty"`
|
||||
Bitmap *Bitmap `protobuf:"bytes,2,opt" json:"Bitmap,omitempty"`
|
||||
N *uint64 `protobuf:"varint,3,opt" json:"N,omitempty"`
|
||||
Pairs []*Pair `protobuf:"bytes,4,rep" json:"Pairs,omitempty"`
|
||||
Profiles []*Profile `protobuf:"bytes,5,rep" json:"Profiles,omitempty"`
|
||||
Err *string `protobuf:"bytes,1,opt,name=Err" json:"Err,omitempty"`
|
||||
Bitmap *Bitmap `protobuf:"bytes,2,opt,name=Bitmap" json:"Bitmap,omitempty"`
|
||||
N *uint64 `protobuf:"varint,3,opt,name=N" json:"N,omitempty"`
|
||||
Pairs []*Pair `protobuf:"bytes,4,rep,name=Pairs" json:"Pairs,omitempty"`
|
||||
Profiles []*Profile `protobuf:"bytes,5,rep,name=Profiles" json:"Profiles,omitempty"`
|
||||
Changed *bool `protobuf:"varint,6,opt,name=Changed" json:"Changed,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -311,12 +315,19 @@ func (m *QueryResponse) GetProfiles() []*Profile {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *QueryResponse) GetChanged() bool {
|
||||
if m != nil && m.Changed != nil {
|
||||
return *m.Changed
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ImportRequest struct {
|
||||
DB *string `protobuf:"bytes,1,req" json:"DB,omitempty"`
|
||||
Frame *string `protobuf:"bytes,2,req" json:"Frame,omitempty"`
|
||||
Slice *uint64 `protobuf:"varint,3,req" json:"Slice,omitempty"`
|
||||
BitmapIDs []uint64 `protobuf:"varint,4,rep" json:"BitmapIDs,omitempty"`
|
||||
ProfileIDs []uint64 `protobuf:"varint,5,rep" json:"ProfileIDs,omitempty"`
|
||||
DB *string `protobuf:"bytes,1,req,name=DB" json:"DB,omitempty"`
|
||||
Frame *string `protobuf:"bytes,2,req,name=Frame" json:"Frame,omitempty"`
|
||||
Slice *uint64 `protobuf:"varint,3,req,name=Slice" json:"Slice,omitempty"`
|
||||
BitmapIDs []uint64 `protobuf:"varint,4,rep,name=BitmapIDs" json:"BitmapIDs,omitempty"`
|
||||
ProfileIDs []uint64 `protobuf:"varint,5,rep,name=ProfileIDs" json:"ProfileIDs,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -360,7 +371,7 @@ func (m *ImportRequest) GetProfileIDs() []uint64 {
|
|||
}
|
||||
|
||||
type ImportResponse struct {
|
||||
Err *string `protobuf:"bytes,1,opt" json:"Err,omitempty"`
|
||||
Err *string `protobuf:"bytes,1,opt,name=Err" json:"Err,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +387,7 @@ func (m *ImportResponse) GetErr() string {
|
|||
}
|
||||
|
||||
type Cache struct {
|
||||
BitmapIDs []uint64 `protobuf:"varint,1,rep" json:"BitmapIDs,omitempty"`
|
||||
BitmapIDs []uint64 `protobuf:"varint,1,rep,name=BitmapIDs" json:"BitmapIDs,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -391,5 +402,34 @@ func (m *Cache) GetBitmapIDs() []uint64 {
|
|||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
type SliceMaxResponse struct {
|
||||
SliceMax *uint64 `protobuf:"varint,1,req,name=SliceMax" json:"SliceMax,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SliceMaxResponse) Reset() { *m = SliceMaxResponse{} }
|
||||
func (m *SliceMaxResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*SliceMaxResponse) ProtoMessage() {}
|
||||
|
||||
func (m *SliceMaxResponse) GetSliceMax() uint64 {
|
||||
if m != nil && m.SliceMax != nil {
|
||||
return *m.SliceMax
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Bitmap)(nil), "internal.Bitmap")
|
||||
proto.RegisterType((*Chunk)(nil), "internal.Chunk")
|
||||
proto.RegisterType((*Pair)(nil), "internal.Pair")
|
||||
proto.RegisterType((*Bit)(nil), "internal.Bit")
|
||||
proto.RegisterType((*Profile)(nil), "internal.Profile")
|
||||
proto.RegisterType((*Attr)(nil), "internal.Attr")
|
||||
proto.RegisterType((*AttrMap)(nil), "internal.AttrMap")
|
||||
proto.RegisterType((*QueryRequest)(nil), "internal.QueryRequest")
|
||||
proto.RegisterType((*QueryResponse)(nil), "internal.QueryResponse")
|
||||
proto.RegisterType((*ImportRequest)(nil), "internal.ImportRequest")
|
||||
proto.RegisterType((*ImportResponse)(nil), "internal.ImportResponse")
|
||||
proto.RegisterType((*Cache)(nil), "internal.Cache")
|
||||
proto.RegisterType((*SliceMaxResponse)(nil), "internal.SliceMaxResponse")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ message QueryResponse {
|
|||
optional uint64 N = 3;
|
||||
repeated Pair Pairs = 4;
|
||||
repeated Profile Profiles = 5;
|
||||
optional bool Changed = 6;
|
||||
}
|
||||
|
||||
message ImportRequest {
|
||||
|
|
@ -68,3 +69,7 @@ message ImportResponse {
|
|||
message Cache {
|
||||
repeated uint64 BitmapIDs = 1;
|
||||
}
|
||||
|
||||
message SliceMaxResponse {
|
||||
required uint64 SliceMax = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,24 +41,28 @@ func NewBitmap(a ...uint64) *Bitmap {
|
|||
}
|
||||
|
||||
// Add adds values to the bitmap.
|
||||
func (b *Bitmap) Add(a ...uint64) error {
|
||||
func (b *Bitmap) Add(a ...uint64) (changed bool, err error) {
|
||||
changed = false
|
||||
for _, v := range a {
|
||||
// Create an add operation.
|
||||
op := &op{typ: opTypeAdd, value: v}
|
||||
|
||||
// Write operation to op log.
|
||||
if err := b.writeOp(op); err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Apply to the in-memory bitmap.
|
||||
op.apply(b)
|
||||
if op.apply(b) {
|
||||
changed = true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
func (b *Bitmap) add(v uint64) {
|
||||
func (b *Bitmap) add(v uint64) bool {
|
||||
hb := highbits(v)
|
||||
i := search64(b.keys, hb)
|
||||
|
||||
|
|
@ -69,7 +73,7 @@ func (b *Bitmap) add(v uint64) {
|
|||
i = -i - 1
|
||||
}
|
||||
|
||||
b.containers[i].add(lowbits(v))
|
||||
return b.containers[i].add(lowbits(v))
|
||||
}
|
||||
|
||||
// Contains returns true if v is in the bitmap.
|
||||
|
|
@ -82,29 +86,32 @@ func (b *Bitmap) Contains(v uint64) bool {
|
|||
}
|
||||
|
||||
// Remove removes values from the bitmap.
|
||||
func (b *Bitmap) Remove(a ...uint64) error {
|
||||
func (b *Bitmap) Remove(a ...uint64) (changed bool, err error) {
|
||||
changed = false
|
||||
for _, v := range a {
|
||||
// Create an add operation.
|
||||
op := &op{typ: opTypeRemove, value: v}
|
||||
|
||||
// Write operation to op log.
|
||||
if err := b.writeOp(op); err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Apply operation to the bitmap.
|
||||
op.apply(b)
|
||||
if op.apply(b) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
func (b *Bitmap) remove(v uint64) {
|
||||
func (b *Bitmap) remove(v uint64) bool {
|
||||
hb := highbits(v)
|
||||
i := search64(b.keys, hb)
|
||||
if i < 0 {
|
||||
return
|
||||
return false
|
||||
}
|
||||
b.containers[i].remove(lowbits(v))
|
||||
return b.containers[i].remove(lowbits(v))
|
||||
}
|
||||
|
||||
// Slice returns a slice of all integers in the bitmap.
|
||||
|
|
@ -429,34 +436,32 @@ func (c *container) unmap() {
|
|||
}
|
||||
|
||||
// add adds a value to the container.
|
||||
func (c *container) add(v uint16) {
|
||||
func (c *container) add(v uint16) bool {
|
||||
if c.isArray() {
|
||||
c.arrayAdd(v)
|
||||
return
|
||||
return c.arrayAdd(v)
|
||||
}
|
||||
c.bitmapAdd(v)
|
||||
return c.bitmapAdd(v)
|
||||
}
|
||||
|
||||
func (c *container) arrayAdd(v uint16) {
|
||||
func (c *container) arrayAdd(v uint16) bool {
|
||||
// Optimize appending to the end of an array container.
|
||||
if c.n > 0 && c.n < arrayMaxSize && c.isArray() && c.array[c.n-1] < v {
|
||||
c.unmap()
|
||||
c.array = append(c.array, v)
|
||||
c.n++
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
// Find index of the integer in the container. Exit if it already exists.
|
||||
i := search16(c.array, v)
|
||||
if i >= 0 {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// Convert to a bitmap container if too many values are in an array container.
|
||||
if c.n >= arrayMaxSize {
|
||||
c.convertToBitmap()
|
||||
c.bitmapAdd(v)
|
||||
return
|
||||
return c.bitmapAdd(v)
|
||||
}
|
||||
|
||||
// Otherwise insert into array.
|
||||
|
|
@ -466,15 +471,17 @@ func (c *container) arrayAdd(v uint16) {
|
|||
copy(c.array[i+1:], c.array[i:])
|
||||
c.array[i] = v
|
||||
c.n++
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *container) bitmapAdd(v uint16) {
|
||||
func (c *container) bitmapAdd(v uint16) bool {
|
||||
if c.bitmapContains(v) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
c.unmap()
|
||||
c.bitmap[v/64] |= (1 << uint64(v%64))
|
||||
c.n++
|
||||
return true
|
||||
}
|
||||
|
||||
// contains returns true if v is in the container.
|
||||
|
|
@ -494,28 +501,28 @@ func (c *container) bitmapContains(v uint16) bool {
|
|||
}
|
||||
|
||||
// remove adds a value to the container.
|
||||
func (c *container) remove(v uint16) {
|
||||
func (c *container) remove(v uint16) bool {
|
||||
if c.isArray() {
|
||||
c.arrayRemove(v)
|
||||
return
|
||||
return c.arrayRemove(v)
|
||||
}
|
||||
c.bitmapRemove(v)
|
||||
return c.bitmapRemove(v)
|
||||
}
|
||||
|
||||
func (c *container) arrayRemove(v uint16) {
|
||||
func (c *container) arrayRemove(v uint16) bool {
|
||||
i := search16(c.array, v)
|
||||
if i < 0 {
|
||||
return
|
||||
return false
|
||||
}
|
||||
c.unmap()
|
||||
|
||||
c.n--
|
||||
c.array = append(c.array[:i], c.array[i+1:]...)
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *container) bitmapRemove(v uint16) {
|
||||
func (c *container) bitmapRemove(v uint16) bool {
|
||||
if !c.bitmapContains(v) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
c.unmap()
|
||||
|
||||
|
|
@ -527,6 +534,7 @@ func (c *container) bitmapRemove(v uint16) {
|
|||
if c.n == arrayMaxSize {
|
||||
c.convertToArray()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// convertToArray converts the values in the bitmap to array values.
|
||||
|
|
@ -594,15 +602,16 @@ type op struct {
|
|||
}
|
||||
|
||||
// apply executes the operation against a bitmap.
|
||||
func (op *op) apply(b *Bitmap) {
|
||||
func (op *op) apply(b *Bitmap) bool {
|
||||
switch op.typ {
|
||||
case opTypeAdd:
|
||||
b.add(op.value)
|
||||
return b.add(op.value)
|
||||
case opTypeRemove:
|
||||
b.remove(op.value)
|
||||
return b.remove(op.value)
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid op type: %d", op.typ))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WriteTo writes op to the w.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue