mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-07 00:55:55 +00:00
fixed conflicts
This commit is contained in:
commit
172f7490f6
15 changed files with 222 additions and 108 deletions
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
flag "github.com/spf13/pflag"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ func setupVersionBuild() {
|
|||
// setAllConfig looks for environment variables which are capitalized versions
|
||||
// of the flag names with dashes replaced by underscores, and prefixed with
|
||||
// envPrefix plus an underscore.
|
||||
func setAllConfig(v *viper.Viper, flags *flag.FlagSet, envPrefix string) error {
|
||||
func setAllConfig(v *viper.Viper, flags *pflag.FlagSet, envPrefix string) error {
|
||||
// add cmd line flag def to viper
|
||||
err := v.BindPFlags(flags)
|
||||
if err != nil {
|
||||
|
|
@ -108,7 +108,7 @@ func setAllConfig(v *viper.Viper, flags *flag.FlagSet, envPrefix string) error {
|
|||
|
||||
// set all values from viper
|
||||
var flagErr error
|
||||
flags.VisitAll(func(f *flag.Flag) {
|
||||
flags.VisitAll(func(f *pflag.Flag) {
|
||||
if flagErr != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// failErr calls t.Fatal if err != nil and adds the optional context to the
|
||||
// error message.
|
||||
func failErr(t *testing.T, err error, context ...string) {
|
||||
ctx := strings.Join(context, "; ")
|
||||
if err != nil {
|
||||
|
|
@ -58,10 +60,16 @@ func ExecNewRootCommand(t *testing.T, args ...string) (string, error) {
|
|||
return string(output), err
|
||||
}
|
||||
|
||||
// validator is a simple helper to avoid repeated `if err != nil` checks in
|
||||
// validation code. One can use it to check that several pairs of things are
|
||||
// equal, and at the end access an informative error message about the first
|
||||
// non-equal pair encountered (or nil if all were equal)
|
||||
type validator struct {
|
||||
err error
|
||||
}
|
||||
|
||||
// Check that two things are equal, and if not set v.err to a descriptive error
|
||||
// message.
|
||||
func (v *validator) Check(actual, expected interface{}) {
|
||||
if v.err != nil {
|
||||
return
|
||||
|
|
@ -70,8 +78,14 @@ func (v *validator) Check(actual, expected interface{}) {
|
|||
v.err = fmt.Errorf("Actual: '%v' is not equal to '%v'", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
// Error returns the validator's error value if any v.Check call found an error.
|
||||
func (v *validator) Error() error { return v.err }
|
||||
|
||||
// commandTest represents all possible ways to configure a a pilosa command, as
|
||||
// well as a function for validating whether the command worked as expected.
|
||||
// args should be set to everything that comes after "pilosa" on the comand
|
||||
// line. See tests like backup_test.go for examples.
|
||||
type commandTest struct {
|
||||
args []string
|
||||
env map[string]string
|
||||
|
|
@ -79,6 +93,29 @@ type commandTest struct {
|
|||
validation func() error
|
||||
}
|
||||
|
||||
// executeDry sets up and executes each commandTest with the --dry-run flag set
|
||||
// to true, and then executes the tests validation function. This stops
|
||||
// execution after PersistentPreRunE (and so before the command's Run or RunE
|
||||
// function is called). This is useful for verifying that configuration happened
|
||||
// properly.
|
||||
func executeDry(t *testing.T, tests []commandTest) {
|
||||
for i, test := range tests {
|
||||
test.args = append(test.args[:1], append([]string{"--dry-run"}, test.args[1:]...)...)
|
||||
com := test.setupCommand(t)
|
||||
err := com.Execute()
|
||||
if err.Error() != "dry run" {
|
||||
t.Fatalf("Problem with test %d, err: '%v'", i, err)
|
||||
}
|
||||
if err := test.validation(); err != nil {
|
||||
t.Fatalf("Failed test %d due to: %v", i, err)
|
||||
}
|
||||
test.reset()
|
||||
}
|
||||
}
|
||||
|
||||
// setupCommand sets up all the configuration specified in the commandTest so
|
||||
// that it can be run. This includes setting environment variables, and creating
|
||||
// a temp config file with the cfgFileContent string as its content.
|
||||
func (ct *commandTest) setupCommand(t *testing.T) *cobra.Command {
|
||||
// make config file
|
||||
cfgFile, err := ioutil.TempFile("", "")
|
||||
|
|
@ -105,27 +142,13 @@ func (ct *commandTest) setupCommand(t *testing.T) *cobra.Command {
|
|||
return rc
|
||||
}
|
||||
|
||||
// reset the environment after setup/run of a commandTest.
|
||||
func (ct *commandTest) reset() {
|
||||
for name, _ := range ct.env {
|
||||
os.Setenv(name, "")
|
||||
}
|
||||
}
|
||||
|
||||
func executeDry(t *testing.T, tests []commandTest) {
|
||||
for i, test := range tests {
|
||||
test.args = append(test.args[:1], append([]string{"--dry-run"}, test.args[1:]...)...)
|
||||
com := test.setupCommand(t)
|
||||
err := com.Execute()
|
||||
if err.Error() != "dry run" {
|
||||
t.Fatalf("Problem with test %d, err: '%v'", i, err)
|
||||
}
|
||||
if err := test.validation(); err != nil {
|
||||
t.Fatalf("Failed test %d due to: %v", i, err)
|
||||
}
|
||||
test.reset()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootCommand(t *testing.T) {
|
||||
outStr, err := ExecNewRootCommand(t, "--help")
|
||||
if !strings.Contains(outStr, "Usage:") ||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ on the configured port.`,
|
|||
flags.StringSliceVarP(&Server.Config.Cluster.Nodes, "cluster.hosts", "", []string{}, "Comma separated list of hosts in cluster.")
|
||||
flags.DurationVarP((*time.Duration)(&Server.Config.Cluster.PollingInterval), "cluster.poll-interval", "", time.Minute, "Polling interval for cluster.") // TODO what actually is this?
|
||||
flags.StringVarP(&Server.Config.Plugins.Path, "plugins.path", "", "", "Path to plugin directory.")
|
||||
flags.StringVar(&Server.Config.LogPath, "log-path", "", "Log path")
|
||||
flags.DurationVarP((*time.Duration)(&Server.Config.AntiEntropy.Interval), "anti-entropy.interval", "", time.Minute*10, "Interval at which to run anti-entropy routine.")
|
||||
flags.StringVarP(&Server.CPUProfile, "profile.cpu", "", "", "Where to store CPU profile.")
|
||||
flags.DurationVarP(&Server.CPUTime, "profile.cpu-time", "", 30*time.Second, "CPU profile duration.")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package cmd_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
|
@ -23,6 +24,8 @@ func TestServerConfig(t *testing.T) {
|
|||
failErr(t, err, "making data dir")
|
||||
profFile, err := ioutil.TempFile("", "")
|
||||
failErr(t, err, "making temp file")
|
||||
logFile, err := ioutil.TempFile("", "")
|
||||
failErr(t, err, "making log file")
|
||||
tests := []commandTest{
|
||||
// TEST 0
|
||||
{
|
||||
|
|
@ -73,7 +76,7 @@ data-dir = "` + actualDataDir + `"
|
|||
},
|
||||
// TEST 2
|
||||
{
|
||||
args: []string{"server"},
|
||||
args: []string{"server", "--log-path", logFile.Name()},
|
||||
env: map[string]string{"PILOSA_PROFILE.CPU_TIME": "1m"},
|
||||
cfgFileContent: `
|
||||
bind = "localhost:0"
|
||||
|
|
@ -96,7 +99,16 @@ data-dir = "` + actualDataDir + `"
|
|||
v.Check(cmd.Server.Config.AntiEntropy.Interval, pilosa.Duration(time.Minute*11))
|
||||
v.Check(cmd.Server.CPUProfile, profFile.Name())
|
||||
v.Check(cmd.Server.CPUTime, time.Minute)
|
||||
return v.Error()
|
||||
v.Check(cmd.Server.Config.LogPath, logFile.Name())
|
||||
if v.Error() != nil {
|
||||
return v.Error()
|
||||
}
|
||||
// confirm log file was written
|
||||
info, err := logFile.Stat()
|
||||
if err != nil || info.Size() == 0 {
|
||||
return errors.New("Log file was not written!")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ type Config struct {
|
|||
AntiEntropy struct {
|
||||
Interval Duration `toml:"interval"`
|
||||
} `toml:"anti-entropy"`
|
||||
|
||||
LogPath string `toml:"log-path"`
|
||||
}
|
||||
|
||||
// NewConfig returns an instance of Config with default options.
|
||||
|
|
|
|||
26
db.go
26
db.go
|
|
@ -86,15 +86,16 @@ func (db *DB) SetColumnLabel(v string) error {
|
|||
db.mu.Lock()
|
||||
defer db.mu.Unlock()
|
||||
|
||||
// Ignore if no change occurred.
|
||||
if v == "" || db.columnLabel == v {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Make sure columnLabel is valid name
|
||||
err := ValidateName(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Ignore if no change occurred.
|
||||
if v == "" || db.columnLabel == v {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Persist meta data to disk on change.
|
||||
db.columnLabel = v
|
||||
|
|
@ -373,14 +374,17 @@ func (db *DB) createFrame(name string, opt FrameOptions) (*Frame, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Update options.
|
||||
if opt.RowLabel != "" {
|
||||
err := ValidateName(opt.RowLabel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Default the time quantum to what is set on the DB.
|
||||
if err := f.SetTimeQuantum(db.timeQuantum); err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set options.
|
||||
if err := f.SetRowLabel(opt.RowLabel); err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
f.SetRowLabel(opt.RowLabel)
|
||||
|
||||
// Add to database's frame lookup.
|
||||
db.frames[name] = f
|
||||
|
|
|
|||
19
db_test.go
19
db_test.go
|
|
@ -34,6 +34,25 @@ func TestDB_CreateFrameIfNotExists(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure database defaults the time quantum on new frames.
|
||||
func TestDB_CreateFrame_TimeQuantum(t *testing.T) {
|
||||
db := MustOpenDB()
|
||||
defer db.Close()
|
||||
|
||||
// Set database time quantum.
|
||||
if err := db.SetTimeQuantum(pilosa.TimeQuantum("YM")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create frame.
|
||||
f, err := db.CreateFrame("f", pilosa.FrameOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if q := f.TimeQuantum(); q != pilosa.TimeQuantum("YM") {
|
||||
t.Fatalf("unexpected frame time quantum: %s", q)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure database can delete a frame.
|
||||
func TestDB_DeleteFrame(t *testing.T) {
|
||||
db := MustOpenDB()
|
||||
|
|
|
|||
76
executor.go
76
executor.go
|
|
@ -490,26 +490,27 @@ func (e *Executor) executeProfile(ctx context.Context, db string, c *pql.Call, o
|
|||
|
||||
// executeClearBit executes a ClearBit() call.
|
||||
func (e *Executor) executeClearBit(ctx context.Context, db string, c *pql.Call, opt *ExecOptions) (bool, error) {
|
||||
view, _ := c.Args["view"].(string)
|
||||
frame, ok := c.Args["frame"].(string)
|
||||
if !ok {
|
||||
return false, errors.New("ClearBit() frame required")
|
||||
}
|
||||
|
||||
// Lookup column label.
|
||||
// Retrieve frame.
|
||||
d := e.Index.DB(db)
|
||||
if d == nil {
|
||||
return false, nil
|
||||
return false, ErrDatabaseNotFound
|
||||
}
|
||||
columnLabel := d.ColumnLabel()
|
||||
|
||||
// Lookup row label.
|
||||
f := e.Index.Frame(db, frame)
|
||||
f := d.Frame(frame)
|
||||
if f == nil {
|
||||
return false, nil
|
||||
return false, ErrFrameNotFound
|
||||
}
|
||||
|
||||
// Retrieve labels.
|
||||
columnLabel := d.ColumnLabel()
|
||||
rowLabel := f.RowLabel()
|
||||
|
||||
// Read row & column ids.
|
||||
// Read fields using labels.
|
||||
rowID, ok := c.Args[rowLabel].(uint64)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("ClearBit() field required: %s", rowLabel)
|
||||
|
|
@ -520,12 +521,38 @@ func (e *Executor) executeClearBit(ctx context.Context, db string, c *pql.Call,
|
|||
return false, fmt.Errorf("ClearBit() field required: %s", columnLabel)
|
||||
}
|
||||
|
||||
// Clear bits for each view.
|
||||
switch view {
|
||||
case ViewStandard:
|
||||
return e.executeClearBitView(ctx, db, c, f, view, colID, rowID, opt)
|
||||
case ViewInverse:
|
||||
return e.executeClearBitView(ctx, db, c, f, view, rowID, colID, opt)
|
||||
case "":
|
||||
var ret bool
|
||||
if changed, err := e.executeClearBitView(ctx, db, c, f, ViewStandard, colID, rowID, opt); err != nil {
|
||||
return ret, err
|
||||
} else if changed {
|
||||
ret = true
|
||||
}
|
||||
if changed, err := e.executeClearBitView(ctx, db, c, f, ViewInverse, rowID, colID, opt); err != nil {
|
||||
return ret, err
|
||||
} else if changed {
|
||||
ret = true
|
||||
}
|
||||
return ret, nil
|
||||
default:
|
||||
return false, fmt.Errorf("invalid view: %s", view)
|
||||
}
|
||||
}
|
||||
|
||||
// executeClearBitView executes a ClearBit() call for a single view.
|
||||
func (e *Executor) executeClearBitView(ctx context.Context, db string, c *pql.Call, f *Frame, view string, colID, rowID uint64, opt *ExecOptions) (bool, error) {
|
||||
slice := colID / SliceWidth
|
||||
ret := false
|
||||
for _, node := range e.Cluster.FragmentNodes(db, slice) {
|
||||
// Update locally if host matches.
|
||||
if node.Host == e.Host {
|
||||
val, err := f.ClearBit(rowID, colID, nil)
|
||||
val, err := f.ClearBit(view, rowID, colID, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if val {
|
||||
|
|
@ -550,6 +577,7 @@ func (e *Executor) executeClearBit(ctx context.Context, db string, c *pql.Call,
|
|||
|
||||
// executeSetBit executes a SetBit() call.
|
||||
func (e *Executor) executeSetBit(ctx context.Context, db string, c *pql.Call, opt *ExecOptions) (bool, error) {
|
||||
view, _ := c.Args["view"].(string)
|
||||
frame, ok := c.Args["frame"].(string)
|
||||
if !ok {
|
||||
return false, errors.New("SetBit() field required: frame")
|
||||
|
|
@ -558,7 +586,7 @@ func (e *Executor) executeSetBit(ctx context.Context, db string, c *pql.Call, op
|
|||
// Retrieve frame.
|
||||
d := e.Index.DB(db)
|
||||
if d == nil {
|
||||
return false, ErrFrameNotFound
|
||||
return false, ErrDatabaseNotFound
|
||||
}
|
||||
f := d.Frame(frame)
|
||||
if f == nil {
|
||||
|
|
@ -590,13 +618,39 @@ func (e *Executor) executeSetBit(ctx context.Context, db string, c *pql.Call, op
|
|||
timestamp = &t
|
||||
}
|
||||
|
||||
// Set bits for each view.
|
||||
switch view {
|
||||
case ViewStandard:
|
||||
return e.executeSetBitView(ctx, db, c, f, view, colID, rowID, timestamp, opt)
|
||||
case ViewInverse:
|
||||
return e.executeSetBitView(ctx, db, c, f, view, rowID, colID, timestamp, opt)
|
||||
case "":
|
||||
var ret bool
|
||||
if changed, err := e.executeSetBitView(ctx, db, c, f, ViewStandard, colID, rowID, timestamp, opt); err != nil {
|
||||
return ret, err
|
||||
} else if changed {
|
||||
ret = true
|
||||
}
|
||||
if changed, err := e.executeSetBitView(ctx, db, c, f, ViewInverse, rowID, colID, timestamp, opt); err != nil {
|
||||
return ret, err
|
||||
} else if changed {
|
||||
ret = true
|
||||
}
|
||||
return ret, nil
|
||||
default:
|
||||
return false, fmt.Errorf("invalid view: %s", view)
|
||||
}
|
||||
}
|
||||
|
||||
// executeSetBitView executes a SetBit() call for a specific view.
|
||||
func (e *Executor) executeSetBitView(ctx context.Context, db string, c *pql.Call, f *Frame, view string, colID, rowID uint64, timestamp *time.Time, opt *ExecOptions) (bool, error) {
|
||||
slice := colID / SliceWidth
|
||||
ret := false
|
||||
|
||||
for _, node := range e.Cluster.FragmentNodes(db, slice) {
|
||||
// Update locally if host matches.
|
||||
if node.Host == e.Host {
|
||||
val, err := f.SetBit(rowID, colID, timestamp)
|
||||
val, err := f.SetBit(view, rowID, colID, timestamp)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if val {
|
||||
|
|
|
|||
|
|
@ -390,16 +390,16 @@ func TestExecutor_Execute_Range(t *testing.T) {
|
|||
}
|
||||
|
||||
// Set bits.
|
||||
f.MustSetBit(1, 2, MustParseTimePtr("1999-12-31 00:00"))
|
||||
f.MustSetBit(1, 3, MustParseTimePtr("2000-01-01 00:00"))
|
||||
f.MustSetBit(1, 4, MustParseTimePtr("2000-01-02 00:00"))
|
||||
f.MustSetBit(1, 5, MustParseTimePtr("2000-02-01 00:00"))
|
||||
f.MustSetBit(1, 6, MustParseTimePtr("2001-01-01 00:00"))
|
||||
f.MustSetBit(1, 7, MustParseTimePtr("2002-01-01 02:00"))
|
||||
f.MustSetBit(pilosa.ViewStandard, 1, 2, MustParseTimePtr("1999-12-31 00:00"))
|
||||
f.MustSetBit(pilosa.ViewStandard, 1, 3, MustParseTimePtr("2000-01-01 00:00"))
|
||||
f.MustSetBit(pilosa.ViewStandard, 1, 4, MustParseTimePtr("2000-01-02 00:00"))
|
||||
f.MustSetBit(pilosa.ViewStandard, 1, 5, MustParseTimePtr("2000-02-01 00:00"))
|
||||
f.MustSetBit(pilosa.ViewStandard, 1, 6, MustParseTimePtr("2001-01-01 00:00"))
|
||||
f.MustSetBit(pilosa.ViewStandard, 1, 7, MustParseTimePtr("2002-01-01 02:00"))
|
||||
|
||||
f.MustSetBit(1, 2, MustParseTimePtr("1999-12-30 00:00")) // too early
|
||||
f.MustSetBit(1, 2, MustParseTimePtr("2002-02-01 00:00")) // too late
|
||||
f.MustSetBit(10, 2, MustParseTimePtr("2001-01-01 00:00")) // different bitmap
|
||||
f.MustSetBit(pilosa.ViewStandard, 1, 2, MustParseTimePtr("1999-12-30 00:00")) // too early
|
||||
f.MustSetBit(pilosa.ViewStandard, 1, 2, MustParseTimePtr("2002-02-01 00:00")) // too late
|
||||
f.MustSetBit(pilosa.ViewStandard, 10, 2, MustParseTimePtr("2001-01-01 00:00")) // different bitmap
|
||||
|
||||
e := NewExecutor(idx.Index, NewCluster(1))
|
||||
if res, err := e.Execute(context.Background(), "d", MustParse(`Range(id=1, frame=f, start="1999-12-31T00:00", end="2002-01-01T03:00")`), nil, nil); err != nil {
|
||||
|
|
|
|||
58
frame.go
58
frame.go
|
|
@ -110,15 +110,17 @@ func (f *Frame) SetRowLabel(v string) error {
|
|||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
// Ignore if no change occurred.
|
||||
if v == "" || f.rowLabel == v {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// Make sure rowLabel is valid name
|
||||
err := ValidateName(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Ignore if no change occurred.
|
||||
if v == "" || f.rowLabel == v {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Persist meta data to disk on change.
|
||||
f.rowLabel = v
|
||||
|
|
@ -353,28 +355,13 @@ func (f *Frame) newView(path, name string) *View {
|
|||
return view
|
||||
}
|
||||
|
||||
// SetBit sets a bit within the frame.
|
||||
func (f *Frame) SetBit(rowID, colID uint64, t *time.Time) (changed bool, err error) {
|
||||
// Set standard layout bits.
|
||||
if v, err := f.setBit(ViewStandard, rowID, colID, t); err != nil {
|
||||
return changed, err
|
||||
} else if v {
|
||||
changed = v
|
||||
// SetBit sets a bit on a view within the frame.
|
||||
func (f *Frame) SetBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) {
|
||||
// Validate view name.
|
||||
if !IsValidView(name) {
|
||||
return false, ErrInvalidView
|
||||
}
|
||||
|
||||
// Set inverse layout bits.
|
||||
// NOTE: The row & col are transposed for the inverted view.
|
||||
if v, err := f.setBit(ViewInverse, colID, rowID, t); err != nil {
|
||||
return changed, err
|
||||
} else if v {
|
||||
changed = v
|
||||
}
|
||||
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
// setBit sets a bit for a given layout (default or inverted).
|
||||
func (f *Frame) setBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) {
|
||||
// Retrieve view. Exit if it doesn't exist.
|
||||
view, err := f.CreateViewIfNotExists(name)
|
||||
if err != nil {
|
||||
|
|
@ -411,27 +398,12 @@ func (f *Frame) setBit(name string, rowID, colID uint64, t *time.Time) (changed
|
|||
}
|
||||
|
||||
// ClearBit clears a bit within the frame.
|
||||
func (f *Frame) ClearBit(rowID, colID uint64, t *time.Time) (changed bool, err error) {
|
||||
// Clear standard layout bits.
|
||||
if v, err := f.clearBit(ViewStandard, rowID, colID, t); err != nil {
|
||||
return changed, err
|
||||
} else if v {
|
||||
changed = v
|
||||
func (f *Frame) ClearBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) {
|
||||
// Validate view name.
|
||||
if !IsValidView(name) {
|
||||
return false, ErrInvalidView
|
||||
}
|
||||
|
||||
// Clear inverse layout bits.
|
||||
// NOTE: The row & col are transposed for the inverted view.
|
||||
if v, err := f.clearBit(ViewInverse, colID, rowID, t); err != nil {
|
||||
return changed, err
|
||||
} else if v {
|
||||
changed = v
|
||||
}
|
||||
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
// clearBit clears a bit for a given layout (default or inverted).
|
||||
func (f *Frame) clearBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) {
|
||||
// Retrieve view. Exit if it doesn't exist.
|
||||
view, err := f.CreateViewIfNotExists(name)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -119,8 +119,8 @@ func (f *Frame) Reopen() error {
|
|||
}
|
||||
|
||||
// MustSetBit sets a bit on the frame. Panic on error.
|
||||
func (f *Frame) MustSetBit(bitmapID, profileID uint64, t *time.Time) (changed bool) {
|
||||
changed, err := f.SetBit(bitmapID, profileID, t)
|
||||
func (f *Frame) MustSetBit(view string, bitmapID, profileID uint64, t *time.Time) (changed bool) {
|
||||
changed, err := f.SetBit(view, bitmapID, profileID, t)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,12 +39,14 @@ func TestHandler_Schema(t *testing.T) {
|
|||
|
||||
if f, err := d0.CreateFrameIfNotExists("f1", pilosa.FrameOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := f.SetBit(0, 0, nil); err != nil {
|
||||
} else if _, err := f.SetBit(pilosa.ViewStandard, 0, 0, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := f.SetBit(pilosa.ViewInverse, 0, 0, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if f, err := d1.CreateFrameIfNotExists("f0", pilosa.FrameOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := f.SetBit(0, 0, nil); err != nil {
|
||||
} else if _, err := f.SetBit(pilosa.ViewStandard, 0, 0, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := d0.CreateFrameIfNotExists("f0", pilosa.FrameOptions{}); err != nil {
|
||||
|
|
@ -57,7 +59,7 @@ func TestHandler_Schema(t *testing.T) {
|
|||
h.ServeHTTP(w, MustNewHTTPRequest("GET", "/schema", nil))
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("unexpected status code: %d", w.Code)
|
||||
} else if body := w.Body.String(); body != `{"dbs":[{"name":"d0","frames":[{"name":"f0"},{"name":"f1","views":[{"name":"inverse"},{"name":"standard"}]}]},{"name":"d1","frames":[{"name":"f0","views":[{"name":"inverse"},{"name":"standard"}]}]}]}`+"\n" {
|
||||
} else if body := w.Body.String(); body != `{"dbs":[{"name":"d0","frames":[{"name":"f0"},{"name":"f1","views":[{"name":"inverse"},{"name":"standard"}]}]},{"name":"d1","frames":[{"name":"f0","views":[{"name":"standard"}]}]}]}`+"\n" {
|
||||
t.Fatalf("unexpected body: %s", body)
|
||||
}
|
||||
}
|
||||
|
|
@ -95,11 +97,11 @@ func TestHandler_MaxSlices_Inverse(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := f0.SetBit((1*SliceWidth)+1, 30, nil); err != nil {
|
||||
if _, err := f0.SetBit(pilosa.ViewInverse, 30, (1*SliceWidth)+1, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := f0.SetBit((1*SliceWidth)+2, 30, nil); err != nil {
|
||||
} else if _, err := f0.SetBit(pilosa.ViewInverse, 30, (1*SliceWidth)+2, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := f0.SetBit((3*SliceWidth)+4, 30, nil); err != nil {
|
||||
} else if _, err := f0.SetBit(pilosa.ViewInverse, 30, (3*SliceWidth)+4, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
@ -107,11 +109,11 @@ func TestHandler_MaxSlices_Inverse(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := f1.SetBit((0*SliceWidth)+1, 40, nil); err != nil {
|
||||
if _, err := f1.SetBit(pilosa.ViewStandard, 40, (0*SliceWidth)+1, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := f1.SetBit((0*SliceWidth)+2, 40, nil); err != nil {
|
||||
} else if _, err := f1.SetBit(pilosa.ViewInverse, 40, (0*SliceWidth)+2, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := f1.SetBit((0*SliceWidth)+4, 40, nil); err != nil {
|
||||
} else if _, err := f1.SetBit(pilosa.ViewInverse, 40, (0*SliceWidth)+4, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ var (
|
|||
ErrFrameExists = errors.New("frame already exists")
|
||||
ErrFrameNotFound = errors.New("frame not found")
|
||||
|
||||
ErrInvalidView = errors.New("invalid veiw")
|
||||
|
||||
ErrName = errors.New("invalid database or frame's name, must match [a-z0-9_-]")
|
||||
|
||||
// ErrFragmentNotFound is returned when a fragment does not exist.
|
||||
|
|
|
|||
|
|
@ -68,7 +68,15 @@ func (m *Command) Run(args ...string) (err error) {
|
|||
}
|
||||
|
||||
// Setup logging output.
|
||||
m.Server.LogOutput = m.Stderr
|
||||
if m.Config.LogPath == "" {
|
||||
m.Server.LogOutput = m.Stderr
|
||||
} else {
|
||||
logFile, err := os.OpenFile(m.Config.LogPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Server.LogOutput = logFile
|
||||
}
|
||||
|
||||
// Configure index.
|
||||
fmt.Fprintf(m.Stderr, "Using data from: %s\n", m.Config.DataDir)
|
||||
|
|
@ -108,7 +116,17 @@ func normalizeHost(host string) (string, error) {
|
|||
|
||||
// Close shuts down the server.
|
||||
func (m *Command) Close() error {
|
||||
err := m.Server.Close()
|
||||
var logErr error
|
||||
serveErr := m.Server.Close()
|
||||
logOutput := m.Server.LogOutput
|
||||
if closer, ok := logOutput.(io.Closer); ok {
|
||||
logErr = closer.Close()
|
||||
}
|
||||
close(m.Done)
|
||||
return err
|
||||
if serveErr != nil && logErr != nil {
|
||||
return fmt.Errorf("closing server: '%v', closing logs: '%v'", serveErr, logErr)
|
||||
} else if logErr != nil {
|
||||
return logErr
|
||||
}
|
||||
return serveErr
|
||||
}
|
||||
|
|
|
|||
5
view.go
5
view.go
|
|
@ -17,6 +17,11 @@ const (
|
|||
ViewInverse = "inverse"
|
||||
)
|
||||
|
||||
// IsValidView returns true if name is valid.
|
||||
func IsValidView(name string) bool {
|
||||
return name == ViewStandard || name == ViewInverse
|
||||
}
|
||||
|
||||
// View represents a container for frame data.
|
||||
type View struct {
|
||||
mu sync.Mutex
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue