Merge branch 'develop' into deadcode

This commit is contained in:
Matt Jaffee 2018-07-01 12:18:28 -05:00
commit 935aaa96f6
No known key found for this signature in database
GPG key ID: 08A3DFFF987B11BF
12 changed files with 557 additions and 749 deletions

View file

@ -48,8 +48,8 @@ build: vendor
# Create a single release build under the build directory
release-build: vendor
$(MAKE) $(if $(DOCKER_BUILD),docker-)build FLAGS="-o build/pilosa-$(VERSION_ID)/pilosa" RELEASE=1
cp NOTICE LICENSE README.md build/pilosa-$(VERSION_ID)
$(if $(ENTERPRISE_ENABLED),cp enterprise/COPYING build/pilosa-$(VERSION_ID))
cp NOTICE README.md build/pilosa-$(VERSION_ID)
$(if $(ENTERPRISE_ENABLED),cp enterprise/COPYING build/pilosa-$(VERSION_ID),cp LICENSE build/pilosa-$(VERSION_ID))
tar -cvz -C build -f build/pilosa-$(VERSION_ID).tar.gz pilosa-$(VERSION_ID)/
@echo Created release build: build/pilosa-$(VERSION_ID).tar.gz

File diff suppressed because it is too large Load diff

View file

@ -583,7 +583,6 @@ func (f *Field) RecalculateCaches() {
// CreateViewIfNotExists returns the named view, creating it if necessary.
// Additionally, a CreateViewMessage is sent to the cluster.
func (f *Field) CreateViewIfNotExists(name string) (*View, error) {
view, created, err := f.createViewIfNotExistsBase(name)
if err != nil {
return nil, err

View file

@ -1752,7 +1752,7 @@ func (s *FragmentSyncer) syncFragment() error {
}
// Retrieve remote blocks.
blocks, err := s.Cluster.InternalClient.FragmentBlocks(context.Background(), nil, s.Fragment.index, s.Fragment.field, s.Fragment.shard)
blocks, err := s.Cluster.InternalClient.FragmentBlocks(context.Background(), &node.URI, s.Fragment.index, s.Fragment.field, s.Fragment.shard)
if err != nil && err != ErrFragmentNotFound {
return errors.Wrap(err, "getting blocks")
}

View file

@ -564,6 +564,8 @@ func (h *Holder) logStartup() error {
// HolderSyncer is an active anti-entropy tool that compares the local holder
// with a remote holder based on block checksums and resolves differences.
type HolderSyncer struct {
mu sync.Mutex
Holder *Holder
Node *Node
@ -588,6 +590,8 @@ func (s *HolderSyncer) IsClosing() bool {
// SyncHolder compares the holder on host with the local holder and resolves differences.
func (s *HolderSyncer) SyncHolder() error {
s.mu.Lock() // only allow one instance of SyncHolder to be running at a time
defer s.mu.Unlock()
ti := time.Now()
// Iterate over schema in sorted order.
for _, di := range s.Holder.Schema() {

View file

@ -24,8 +24,6 @@ import (
"testing"
"github.com/pilosa/pilosa"
"github.com/pilosa/pilosa/http"
"github.com/pilosa/pilosa/pql"
"github.com/pilosa/pilosa/test"
)
@ -350,49 +348,40 @@ func TestHolder_DeleteIndex(t *testing.T) {
// Ensure holder can sync with a remote holder.
func TestHolderSyncer_SyncHolder(t *testing.T) {
t.Skip() // Until test.NewServer() works
s := test.NewServer()
defer s.Close()
uri, err := pilosa.NewURIFromAddress(s.URL)
c := test.MustNewCluster(t, 2)
c[0].Config.Cluster.ReplicaN = 2
c[0].Config.AntiEntropy.Interval = 0
c[1].Config.Cluster.ReplicaN = 2
c[1].Config.AntiEntropy.Interval = 0
err := c.Start()
if err != nil {
t.Fatal(err)
t.Fatalf("starting cluster: %v", err)
}
defer c.Close()
_, err = c[0].API.CreateIndex(context.Background(), "i", pilosa.IndexOptions{})
if err != nil {
t.Fatalf("creating index i: %v", err)
}
_, err = c[0].API.CreateIndex(context.Background(), "y", pilosa.IndexOptions{})
if err != nil {
t.Fatalf("creating index y: %v", err)
}
_, err = c[0].API.CreateField(context.Background(), "i", "f", pilosa.OptFieldTypeSet(pilosa.DefaultCacheType, pilosa.DefaultCacheSize))
if err != nil {
t.Fatalf("creating field f: %v", err)
}
_, err = c[0].API.CreateField(context.Background(), "i", "f0", pilosa.OptFieldTypeSet(pilosa.DefaultCacheType, pilosa.DefaultCacheSize))
if err != nil {
t.Fatalf("creating field f0: %v", err)
}
_, err = c[0].API.CreateField(context.Background(), "y", "z", pilosa.OptFieldTypeSet(pilosa.DefaultCacheType, pilosa.DefaultCacheSize))
if err != nil {
t.Fatalf("creating field z in y: %v", err)
}
cluster := test.NewCluster(2)
client := http.GetHTTPClient(nil)
httpClient := http.NewInternalClientFromURI(uri, client)
cluster.InternalClient = httpClient
// Create a local holder.
hldr0 := test.MustOpenHolder()
defer hldr0.Close()
// Create a remote holder wrapped by an HTTP
hldr1 := test.MustOpenHolder()
defer hldr1.Close()
s.Handler.API.Holder = hldr1.Holder
s.Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, shards []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
e := pilosa.NewExecutor(pilosa.OptExecutorInternalQueryClient(httpClient))
e.Holder = hldr1.Holder
e.Node = cluster.Nodes[1]
e.Cluster = cluster
return e.Execute(ctx, index, query, shards, opt)
}
// Mock 2-node, fully replicated cluster.
cluster.ReplicaN = 2
cluster.Nodes[0].URI = pilosa.NewTestURIFromHostPort("localhost", 0)
cluster.Nodes[1].URI = *uri
// Create fields on nodes.
for _, hldr := range []*test.Holder{hldr0, hldr1} {
hldr.MustCreateFieldIfNotExists("i", "f")
hldr.MustCreateFieldIfNotExists("i", "f0")
hldr.MustCreateFieldIfNotExists("y", "z")
}
hldr0 := &test.Holder{Holder: c[0].Server.Holder()}
hldr1 := &test.Holder{Holder: c[1].Server.Holder()}
// Set data on the local holder.
hldr0.SetBit("i", "f", 0, 10)
@ -414,42 +403,39 @@ func TestHolderSyncer_SyncHolder(t *testing.T) {
hldr1.SetBit("y", "z", 10, (3*ShardWidth)+5)
hldr1.SetBit("y", "z", 10, (3*ShardWidth)+7)
// Set highest shard.
hldr0.Index("i").SetRemoteMaxShard(1)
hldr0.Index("y").SetRemoteMaxShard(3)
// Set up syncer.
syncer := pilosa.HolderSyncer{
Holder: hldr0.Holder,
Node: cluster.Nodes[0],
Cluster: cluster,
Stats: pilosa.NopStatsClient,
err = c[0].Server.SyncData()
if err != nil {
t.Fatalf("syncing node 0: %v", err)
}
if err := syncer.SyncHolder(); err != nil {
t.Fatal(err)
err = c[1].Server.SyncData()
if err != nil {
t.Fatalf("syncing node 1: %v", err)
}
// Verify data is the same on both nodes.
for i, hldr := range []*test.Holder{hldr0, hldr1} {
if a := hldr.Row("i", "f", 0).Columns(); !reflect.DeepEqual(a, []uint64{10, 4000}) {
t.Fatalf("unexpected columns(%d/0): %+v", i, a)
} else if a := hldr.Row("i", "f", 2).Columns(); !reflect.DeepEqual(a, []uint64{20}) {
t.Fatalf("unexpected columns(%d/2): %+v", i, a)
} else if a := hldr.Row("i", "f", 3).Columns(); !reflect.DeepEqual(a, []uint64{10}) {
t.Fatalf("unexpected columns(%d/3): %+v", i, a)
} else if a := hldr.Row("i", "f", 120).Columns(); !reflect.DeepEqual(a, []uint64{10}) {
t.Fatalf("unexpected columns(%d/120): %+v", i, a)
} else if a := hldr.Row("i", "f", 200).Columns(); !reflect.DeepEqual(a, []uint64{4}) {
t.Fatalf("unexpected columns(%d/200): %+v", i, a)
t.Errorf("unexpected columns(%d/0): %+v", i, a)
}
if a := hldr.Row("i", "f", 2).Columns(); !reflect.DeepEqual(a, []uint64{20}) {
t.Errorf("unexpected columns(%d/2): %+v", i, a)
}
if a := hldr.Row("i", "f", 3).Columns(); !reflect.DeepEqual(a, []uint64{10}) {
t.Errorf("unexpected columns(%d/3): %+v", i, a)
}
if a := hldr.Row("i", "f", 120).Columns(); !reflect.DeepEqual(a, []uint64{10}) {
t.Errorf("unexpected columns(%d/120): %+v", i, a)
}
if a := hldr.Row("i", "f", 200).Columns(); !reflect.DeepEqual(a, []uint64{4}) {
t.Errorf("unexpected columns(%d/200): %+v", i, a)
}
if a := hldr.Row("i", "f0", 9).Columns(); !reflect.DeepEqual(a, []uint64{ShardWidth + 5}) {
t.Fatalf("unexpected columns(%d/d/f0): %+v", i, a)
t.Errorf("unexpected columns(%d/d/f0): %+v", i, a)
}
if a := hldr.Row("y", "z", 10).Columns(); !reflect.DeepEqual(a, []uint64{(3 * ShardWidth) + 4, (3 * ShardWidth) + 5, (3 * ShardWidth) + 7}) {
t.Fatalf("unexpected columns(%d/y/z): %+v", i, a)
t.Errorf("unexpected columns(%d/y/z): %+v", i, a)
}
}
}

View file

@ -717,6 +717,9 @@ func (c *InternalClient) FragmentBlocks(ctx context.Context, uri *pilosa.URI, in
// BlockData returns row/column id pairs for a block.
func (c *InternalClient) BlockData(ctx context.Context, uri *pilosa.URI, index, field string, shard uint64, block int) ([]uint64, []uint64, error) {
if uri == nil {
panic("need to pass a URI to BlockData")
}
buf, err := proto.Marshal(&internal.BlockDataRequest{
Index: index,
Field: field,
@ -727,7 +730,7 @@ func (c *InternalClient) BlockData(ctx context.Context, uri *pilosa.URI, index,
return nil, nil, errors.Wrap(err, "marshaling")
}
u := uriPathToURL(c.defaultURI, "/fragment/block/data")
u := uriPathToURL(uri, "/fragment/block/data")
req, err := http.NewRequest("GET", u.String(), bytes.NewReader(buf))
if err != nil {
return nil, nil, errors.Wrap(err, "creating request")

View file

@ -26,23 +26,10 @@ import (
"github.com/pilosa/pilosa/http"
"github.com/pilosa/pilosa/internal"
"github.com/pilosa/pilosa/pql"
"github.com/pilosa/pilosa/server"
"github.com/pilosa/pilosa/test"
)
func createCluster(c *pilosa.Cluster) ([]*test.Server, []*test.Holder) {
numNodes := len(c.Nodes)
hldr := make([]*test.Holder, numNodes)
server := make([]*test.Server, numNodes)
for i := 0; i < numNodes; i++ {
hldr[i] = test.MustOpenHolder()
server[i] = test.NewServer()
server[i].Handler.API.Cluster = c
server[i].Handler.API.Cluster.Nodes[i].URI = server[i].HostURI()
server[i].Handler.API.Holder = hldr[i].Holder
}
return server, hldr
}
var defaultClient *gohttp.Client
func init() {
@ -52,39 +39,19 @@ func init() {
// Test distributed TopN Row count across 3 nodes.
func TestClient_MultiNode(t *testing.T) {
t.Skip() // Until test.NewServer() works
c := test.MustRunCluster(t, 3,
[]server.CommandOption{
server.OptCommandServerOptions(pilosa.OptServerNodeID("node0"), pilosa.OptServerClusterHasher(&test.ModHasher{}))},
[]server.CommandOption{
server.OptCommandServerOptions(pilosa.OptServerNodeID("node1"), pilosa.OptServerClusterHasher(&test.ModHasher{}))},
[]server.CommandOption{
server.OptCommandServerOptions(pilosa.OptServerNodeID("node2"), pilosa.OptServerClusterHasher(&test.ModHasher{}))},
)
defer c.Close()
cluster := test.NewCluster(3)
s, hldr := createCluster(cluster)
for i := 0; i < len(cluster.Nodes); i++ {
defer hldr[i].Close()
defer s[i].Close()
}
s[0].Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, shards []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
httpClient := http.NewInternalClientFromURI(&cluster.Nodes[0].URI, defaultClient)
e := pilosa.NewExecutor(pilosa.OptExecutorInternalQueryClient(httpClient))
e.Holder = hldr[0].Holder
e.Node = cluster.Nodes[0]
e.Cluster = cluster
return e.Execute(ctx, index, query, shards, opt)
}
s[1].Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, shards []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
httpClient := http.NewInternalClientFromURI(&cluster.Nodes[0].URI, defaultClient)
e := pilosa.NewExecutor(pilosa.OptExecutorInternalQueryClient(httpClient))
e.Holder = hldr[1].Holder
e.Node = cluster.Nodes[1]
e.Cluster = cluster
return e.Execute(ctx, index, query, shards, opt)
}
s[2].Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, shards []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
httpClient := http.NewInternalClientFromURI(&cluster.Nodes[0].URI, defaultClient)
e := pilosa.NewExecutor(pilosa.OptExecutorInternalQueryClient(httpClient))
e.Holder = hldr[2].Holder
e.Node = cluster.Nodes[2]
e.Cluster = cluster
return e.Execute(ctx, index, query, shards, opt)
hldr := []test.Holder{}
for _, command := range c {
hldr = append(hldr, test.Holder{Holder: command.Server.Holder()})
}
// Create a dispersed set of bitmaps across 3 nodes such that each individual node and shard width increment would reveal a different TopN.
@ -106,7 +73,7 @@ func TestClient_MultiNode(t *testing.T) {
}
}
if !ownsNum {
t.Fatalf("Trying to use shard %d on host %s, but it doesn't own that shard. It owns %v", num, s[i].Host(), owns)
t.Fatalf("Trying to use shard %d on host %s, but it doesn't own that shard. It owns %v", num, c[i].URL(), owns)
}
}
@ -120,13 +87,21 @@ func TestClient_MultiNode(t *testing.T) {
maxShard = x
}
}
_, err := c[0].API.CreateIndex(context.Background(), "i", pilosa.IndexOptions{})
if err != nil {
t.Fatalf("creating index: %v", err)
}
_, err = c[0].API.CreateField(context.Background(), "i", "f", pilosa.OptFieldTypeSet(pilosa.DefaultCacheType, 100))
if err != nil {
t.Fatalf("creating field: %v", err)
}
hldr[0].MustSetBits("i", "f", 100, baseBit0+10)
hldr[0].MustSetBits("i", "f", 4, baseBit0+10, baseBit0+11, baseBit0+12)
hldr[0].MustSetBits("i", "f", 4, baseBit0+10, baseBit0+11, baseBit0+12, baseBit0+13, baseBit0+14, baseBit0+15)
hldr[0].MustSetBits("i", "f", 2, baseBit0+1, baseBit0+2, baseBit0+3, baseBit0+4)
hldr[0].MustSetBits("i", "f", 3, baseBit0+1, baseBit0+2, baseBit0+3, baseBit0+4, baseBit0+5)
hldr[0].MustSetBits("i", "f", 22, baseBit0+1, baseBit0+2, baseBit0+10)
hldr[0].MustSetBits("i", "f", 22, baseBit0+1, baseBit0+2)
hldr[1].MustSetBits("i", "f", 99, baseBit1+1, baseBit1+2, baseBit1+3, baseBit1+4)
hldr[1].MustSetBits("i", "f", 100, baseBit1+1, baseBit1+2, baseBit1+3, baseBit1+4, baseBit1+5, baseBit1+6, baseBit1+7, baseBit1+8, baseBit1+9, baseBit1+10)
@ -145,39 +120,27 @@ func TestClient_MultiNode(t *testing.T) {
// Rebuild the RankCache.
// We have to do this to avoid the 10-second cache invalidation delay
// built into cache.Invalidate()
hldr[0].MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, shardNums[0]).RecalculateCache()
hldr[1].MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, shardNums[1]).RecalculateCache()
hldr[2].MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, shardNums[2]).RecalculateCache()
c[0].RecalculateCaches()
c[1].RecalculateCaches()
c[2].RecalculateCaches()
// Connect to each node to compare results.
client := make([]*Client, 3)
client[0] = MustNewClient(s[0].Host(), defaultClient)
client[1] = MustNewClient(s[1].Host(), defaultClient)
client[2] = MustNewClient(s[2].Host(), defaultClient)
client[0] = MustNewClient(c[0].URL(), defaultClient)
client[1] = MustNewClient(c[1].URL(), defaultClient)
client[2] = MustNewClient(c[2].URL(), defaultClient)
topN := 4
queryRequest := &internal.QueryRequest{
Query: fmt.Sprintf(`TopN(f, n=%d)`, topN),
Remote: false,
}
result, err := client[0].Query(context.Background(), "i", queryRequest)
if err != nil {
t.Fatal(err)
}
// Check the results before every node has the correct max shard value.
pairs := result.Results[0].Pairs
for _, pair := range pairs {
if pair.ID == 22 && pair.Count != 3 {
t.Fatalf("Invalid Cluster wide MaxShard prevents accurate calculation of %s", pair)
}
}
// Set max shard to correct value.
hldr[0].Index("i").SetRemoteMaxShard(maxShard)
hldr[1].Index("i").SetRemoteMaxShard(maxShard)
hldr[2].Index("i").SetRemoteMaxShard(maxShard)
result, err = client[0].Query(context.Background(), "i", queryRequest)
if err != nil {
t.Fatal(err)
@ -189,7 +152,7 @@ func TestClient_MultiNode(t *testing.T) {
}
p := []*internal.Pair{
{ID: 100, Count: 12},
{ID: 22, Count: 11},
{ID: 22, Count: 10},
{ID: 98, Count: 8},
{ID: 99, Count: 7}}

View file

@ -70,6 +70,7 @@ type Server struct {
diagnosticInterval time.Duration
maxWritesPerRequest int
isCoordinator bool
syncer HolderSyncer
primaryTranslateStore TranslateStore
@ -209,6 +210,20 @@ func OptServerIsCoordinator(is bool) ServerOption {
}
}
func OptServerNodeID(nodeID string) ServerOption {
return func(s *Server) error {
s.nodeID = nodeID
return nil
}
}
func OptServerClusterHasher(h Hasher) ServerOption {
return func(s *Server) error {
s.cluster.Hasher = h
return nil
}
}
// NewServer returns a new instance of Server.
func NewServer(opts ...ServerOption) (*Server, error) {
s := &Server{
@ -328,6 +343,12 @@ func (s *Server) Open() error {
// buffered channel.
s.cluster.listenForJoins()
s.syncer.Holder = s.holder
s.syncer.Node = s.cluster.Node
s.syncer.Cluster = s.cluster
s.syncer.Closing = s.closing
s.syncer.Stats = s.holder.Stats.WithTags("HolderSyncer")
// Start background monitoring.
s.wg.Add(3)
go func() { defer s.wg.Done(); s.monitorAntiEntropy() }()
@ -370,12 +391,22 @@ func (s *Server) loadNodeID() string {
return nodeID
}
// SyncData manually invokes the anti entropy process which makes sure that this
// node has the data from all replicas across the cluster.
func (s *Server) SyncData() error {
return errors.Wrap(s.syncer.SyncHolder(), "syncing holder")
}
func (s *Server) monitorAntiEntropy() {
if s.antiEntropyInterval == 0 {
return // anti entropy disabled
}
ticker := time.NewTicker(s.antiEntropyInterval)
defer ticker.Stop()
s.logger.Printf("holder sync monitor initializing (%s interval)", s.antiEntropyInterval)
// Initialize syncer with local holder and remote client.
for {
// Wait for tick or a close.
select {
@ -385,18 +416,10 @@ func (s *Server) monitorAntiEntropy() {
s.holder.Stats.Count("AntiEntropy", 1, 1.0)
}
t := time.Now()
s.logger.Printf("holder sync beginning")
// Initialize syncer with local holder and remote client.
var syncer HolderSyncer
syncer.Holder = s.holder
syncer.Node = s.cluster.Node
syncer.Cluster = s.cluster
syncer.Closing = s.closing
syncer.Stats = s.holder.Stats.WithTags("HolderSyncer")
// Sync holders.
if err := syncer.SyncHolder(); err != nil {
s.logger.Printf("holder sync beginning")
if err := s.syncer.SyncHolder(); err != nil {
s.logger.Printf("holder sync error: err=%s", err)
continue
}

View file

@ -1,51 +0,0 @@
// Copyright 2017 Pilosa Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pilosa_test
import (
"context"
"testing"
"time"
"github.com/pilosa/pilosa"
"github.com/pilosa/pilosa/server"
"github.com/pilosa/pilosa/test"
)
// TestMonitorAntiEntropy is a regression test which which caught a bug where
// pilosa.Server was not having its remoteClient field set by an option and so
// it was using a nil client in monitorAntiEntropy.
func TestMonitorAntiEntropy(t *testing.T) {
cluster := test.MustRunCluster(t, 3, []server.CommandOption{test.OptAntiEntropyInterval(time.Millisecond * 20)})
client := cluster[1].Client()
err := client.CreateIndex(context.Background(), "balh", pilosa.IndexOptions{})
if err != nil {
t.Fatalf("creating index: %v", err)
}
err = client.CreateField(context.Background(), "balh", "fralh")
if err != nil {
t.Fatalf("creating field: %v", err)
}
time.Sleep(time.Millisecond * 40)
for _, m := range cluster {
err := m.Close()
if err != nil {
t.Fatal(err)
}
}
}

View file

@ -21,6 +21,11 @@ import (
"github.com/pilosa/pilosa"
)
// modHasher represents a simple, mod-based hashing.
type ModHasher struct{}
func (*ModHasher) Hash(key uint64, n int) int { return int(key) % n }
// NewCluster returns a cluster with n nodes and uses a mod-based hasher.
func NewCluster(n int) *pilosa.Cluster {
path, err := ioutil.TempDir("", "pilosa-cluster-")
@ -30,7 +35,7 @@ func NewCluster(n int) *pilosa.Cluster {
c := pilosa.NewCluster()
c.ReplicaN = 1
c.Hasher = newModHasher()
c.Hasher = &ModHasher{}
c.Path = path
c.Topology = pilosa.NewTopology()
@ -48,14 +53,6 @@ func NewCluster(n int) *pilosa.Cluster {
return c
}
// modHasher represents a simple, mod-based hashing.
type modHasher struct{}
// newModHasher returns a new instance of ModHasher with n buckets.
func newModHasher() *modHasher { return &modHasher{} }
func (*modHasher) Hash(key uint64, n int) int { return int(key) % n }
// newURI is a test URI creator that intentionally swallows errors.
func newURI(scheme, host string, port uint16) pilosa.URI {
uri := pilosa.DefaultURI()

View file

@ -121,6 +121,15 @@ func (h *Holder) Row(index, field string, rowID uint64) *pilosa.Row {
return row
}
func (h *Holder) RowAttrStore(index, field string) pilosa.AttrStore {
idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{})
f, err := idx.CreateFieldIfNotExists(field, pilosa.FieldOptions{})
if err != nil {
panic(err)
}
return f.RowAttrStore()
}
// ViewRow returns a Row for a given field and view.
func (h *Holder) ViewRow(index, field, view string, rowID uint64) *pilosa.Row {
idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{})
@ -142,7 +151,10 @@ func (h *Holder) SetBit(index, field string, rowID, columnID uint64) {
if err != nil {
panic(err)
}
f.SetBit(rowID, columnID, nil)
_, err = f.SetBit(rowID, columnID, nil)
if err != nil {
panic(err)
}
}
// ClearBit clears a bit on the given field.
@ -152,7 +164,10 @@ func (h *Holder) ClearBit(index, field string, rowID, columnID uint64) {
if err != nil {
panic(err)
}
f.ClearBit(rowID, columnID)
_, err = f.ClearBit(rowID, columnID)
if err != nil {
panic(err)
}
}
// MustSetBits sets columns on a row. Panic on error.