diff options
214 files changed, 2309 insertions, 1733 deletions
diff --git a/client/go/internal/admin/prog/valgrind.go b/client/go/internal/admin/prog/valgrind.go index 7d3fb059f8f..2d7f0a597d9 100644 --- a/client/go/internal/admin/prog/valgrind.go +++ b/client/go/internal/admin/prog/valgrind.go @@ -22,9 +22,10 @@ func (p *Spec) ConfigureValgrind() { p.shouldUseValgrind = false p.shouldUseCallgrind = false env := p.Getenv(envvars.VESPA_USE_VALGRIND) + allValgrind := env == "all" parts := strings.Split(env, " ") for _, part := range parts { - if p.BaseName == part { + if p.BaseName == part || allValgrind { trace.Trace("using valgrind as", p.Program, "has basename in", envvars.VESPA_USE_VALGRIND, "=>", env) backticks := util.BackTicksWithStderr out, err := backticks.Run(VALGRIND_PROG, "--help") diff --git a/client/go/internal/admin/prog/valgrind_test.go b/client/go/internal/admin/prog/valgrind_test.go index 6ec622277c6..11d9424405f 100644 --- a/client/go/internal/admin/prog/valgrind_test.go +++ b/client/go/internal/admin/prog/valgrind_test.go @@ -52,6 +52,11 @@ func TestValgrindDetection(t *testing.T) { assert.Equal(t, false, spec.shouldUseValgrind) assert.Equal(t, false, spec.shouldUseCallgrind) + t.Setenv("VESPA_USE_VALGRIND", "all") + spec.ConfigureValgrind() + assert.Equal(t, true, spec.shouldUseValgrind) + assert.Equal(t, false, spec.shouldUseCallgrind) + t.Setenv("VESPA_USE_VALGRIND", "foo bar") spec.ConfigureValgrind() assert.Equal(t, false, spec.shouldUseValgrind) diff --git a/client/go/internal/admin/vespa-wrapper/startcbinary/valgrind.go b/client/go/internal/admin/vespa-wrapper/startcbinary/valgrind.go index 43a1ed602bd..cccb37df8e5 100644 --- a/client/go/internal/admin/vespa-wrapper/startcbinary/valgrind.go +++ b/client/go/internal/admin/vespa-wrapper/startcbinary/valgrind.go @@ -18,9 +18,10 @@ func (p *ProgSpec) configureValgrind() { p.shouldUseValgrind = false p.shouldUseCallgrind = false env := p.getenv(envvars.VESPA_USE_VALGRIND) + allValgrind := env == "all" parts := strings.Split(env, " ") for _, part := range parts { - if p.BaseName == part { + if p.BaseName == part || allValgrind { trace.Trace("using valgrind as", p.Program, "has basename in", envvars.VESPA_USE_VALGRIND, "=>", env) backticks := util.BackTicksWithStderr out, err := backticks.Run("which", "valgrind") diff --git a/client/go/internal/admin/vespa-wrapper/startcbinary/valgrind_test.go b/client/go/internal/admin/vespa-wrapper/startcbinary/valgrind_test.go index 48cc78474ed..1a105d66c4a 100644 --- a/client/go/internal/admin/vespa-wrapper/startcbinary/valgrind_test.go +++ b/client/go/internal/admin/vespa-wrapper/startcbinary/valgrind_test.go @@ -52,6 +52,11 @@ func TestValgrindDetection(t *testing.T) { assert.Equal(t, false, spec.shouldUseValgrind) assert.Equal(t, false, spec.shouldUseCallgrind) + t.Setenv("VESPA_USE_VALGRIND", "all") + spec.configureValgrind() + assert.Equal(t, true, spec.shouldUseValgrind) + assert.Equal(t, false, spec.shouldUseCallgrind) + t.Setenv("VESPA_USE_VALGRIND", "foo bar") spec.configureValgrind() assert.Equal(t, false, spec.shouldUseValgrind) diff --git a/client/go/internal/cli/auth/zts/zts.go b/client/go/internal/cli/auth/zts/zts.go index 1e84912a271..caa2d03367d 100644 --- a/client/go/internal/cli/auth/zts/zts.go +++ b/client/go/internal/cli/auth/zts/zts.go @@ -37,7 +37,7 @@ func (c *Client) AccessToken(domain string, certificate tls.Certificate) (string if err != nil { return "", err } - util.SetCertificate(c.client, []tls.Certificate{certificate}) + util.SetCertificates(c.client, []tls.Certificate{certificate}) response, err := c.client.Do(req, 10*time.Second) if err != nil { return "", err diff --git a/client/go/internal/cli/cmd/config.go b/client/go/internal/cli/cmd/config.go index fd049864096..2d32c454842 100644 --- a/client/go/internal/cli/cmd/config.go +++ b/client/go/internal/cli/cmd/config.go @@ -544,12 +544,12 @@ func (c *Config) list(includeUnset bool) []string { } // flagValue returns the set value and default value of the named flag. -func (c *Config) flagValue(name string) (string, string) { +func (c *Config) flagValue(name string) (string, string, bool) { f, ok := c.flags[name] if !ok { - return "", "" + return "", "", ok } - return f.Value.String(), f.DefValue + return f.Value.String(), f.DefValue, f.Changed } // getNonEmpty returns value of given option, if that value is non-empty @@ -564,9 +564,9 @@ func (c *Config) getNonEmpty(option string) (string, bool) { // get returns the value associated with option, from the most preferred source in the following order: flag > local // config > global config. func (c *Config) get(option string) (string, bool) { - flagValue, flagDefault := c.flagValue(option) + flagValue, flagDefault, changed := c.flagValue(option) // explicit flag value always takes precedence over everything else - if flagValue != flagDefault { + if changed { return flagValue, true } // ... then local config, if option is explicitly defined there diff --git a/client/go/internal/cli/cmd/config_test.go b/client/go/internal/cli/cmd/config_test.go index 612904061de..458878b4356 100644 --- a/client/go/internal/cli/cmd/config_test.go +++ b/client/go/internal/cli/cmd/config_test.go @@ -28,6 +28,7 @@ func TestConfig(t *testing.T) { assertConfigCommand(t, configHome, "", "config", "set", "target", "http://127.0.0.1:8080") assertConfigCommand(t, configHome, "", "config", "set", "target", "https://127.0.0.1") assertConfigCommand(t, configHome, "target = https://127.0.0.1\n", "config", "get", "target") + assertConfigCommand(t, configHome, "target = local\n", "config", "get", "-t", "local", "target") // application assertConfigCommandErr(t, configHome, "Error: invalid application: \"foo\"\n", "config", "set", "application", "foo") diff --git a/client/go/internal/cli/cmd/feed.go b/client/go/internal/cli/cmd/feed.go index c8e032929b8..895a22d2be5 100644 --- a/client/go/internal/cli/cmd/feed.go +++ b/client/go/internal/cli/cmd/feed.go @@ -9,17 +9,20 @@ import ( "time" "github.com/spf13/cobra" + "github.com/vespa-engine/vespa/client/go/internal/util" + "github.com/vespa-engine/vespa/client/go/internal/vespa" "github.com/vespa-engine/vespa/client/go/internal/vespa/document" ) -func addFeedFlags(cmd *cobra.Command, concurrency *int) { - // TOOD(mpolden): Remove this flag - cmd.PersistentFlags().IntVarP(concurrency, "concurrency", "T", 64, "Number of goroutines to use for dispatching") +func addFeedFlags(cmd *cobra.Command, verbose *bool, connections *int) { + cmd.PersistentFlags().IntVarP(connections, "connections", "N", 8, "The number of connections to use") + cmd.PersistentFlags().BoolVarP(verbose, "verbose", "v", false, "Verbose mode. Print errors as they happen") } func newFeedCmd(cli *CLI) *cobra.Command { var ( - concurrency int + verbose bool + connections int ) cmd := &cobra.Command{ Use: "feed FILE", @@ -27,42 +30,66 @@ func newFeedCmd(cli *CLI) *cobra.Command { Long: `Feed documents to a Vespa cluster. A high performance feeding client. This can be used to feed large amounts of -documents to Vespa cluster efficiently. +documents to a Vespa cluster efficiently. The contents of FILE must be either a JSON array or JSON objects separated by newline (JSONL). + +If FILE is a single dash ('-'), documents will be read from standard input. `, Example: `$ vespa feed documents.jsonl +$ cat documents.jsonl | vespa feed - `, Args: cobra.ExactArgs(1), DisableAutoGenTag: true, SilenceUsage: true, Hidden: true, // TODO(mpolden): Remove when ready for public use RunE: func(cmd *cobra.Command, args []string) error { - f, err := os.Open(args[0]) - if err != nil { - return err + var r io.Reader + if args[0] == "-" { + r = cli.Stdin + } else { + f, err := os.Open(args[0]) + if err != nil { + return err + } + defer f.Close() + r = f } - defer f.Close() - return feed(f, cli, concurrency) + return feed(r, cli, verbose, connections) }, } - addFeedFlags(cmd, &concurrency) + addFeedFlags(cmd, &verbose, &connections) return cmd } -func feed(r io.Reader, cli *CLI, concurrency int) error { +func createServiceClients(service *vespa.Service, n int) []util.HTTPClient { + clients := make([]util.HTTPClient, 0, n) + for i := 0; i < n; i++ { + client := service.Client().Clone() + util.ForceHTTP2(client, service.TLSOptions.KeyPair) // Feeding should always use HTTP/2 + clients = append(clients, client) + } + return clients +} + +func feed(r io.Reader, cli *CLI, verbose bool, connections int) error { service, err := documentService(cli) if err != nil { return err } + clients := createServiceClients(service, connections) client := document.NewClient(document.ClientOptions{ BaseURL: service.BaseURL, - }, service) - throttler := document.NewThrottler() + }, clients) + throttler := document.NewThrottler(connections) // TODO(mpolden): Make doom duration configurable circuitBreaker := document.NewCircuitBreaker(10*time.Second, 0) - dispatcher := document.NewDispatcher(client, throttler, circuitBreaker) + errWriter := io.Discard + if verbose { + errWriter = cli.Stderr + } + dispatcher := document.NewDispatcher(client, throttler, circuitBreaker, errWriter) dec := document.NewDecoder(r) start := cli.now() diff --git a/client/go/internal/cli/cmd/feed_test.go b/client/go/internal/cli/cmd/feed_test.go index 1bf1ef6ab9b..521d2b2abd0 100644 --- a/client/go/internal/cli/cmd/feed_test.go +++ b/client/go/internal/cli/cmd/feed_test.go @@ -1,6 +1,7 @@ package cmd import ( + "bytes" "os" "path/filepath" "testing" @@ -42,7 +43,7 @@ func TestFeed(t *testing.T) { require.Nil(t, cli.Run("feed", jsonFile)) assert.Equal(t, "", stderr.String()) - assert.Equal(t, `{ + want := `{ "feeder.seconds": 1.000, "feeder.ok.count": 1, "feeder.ok.rate": 1.000, @@ -63,5 +64,15 @@ func TestFeed(t *testing.T) { "200": 1 } } -`, stdout.String()) +` + assert.Equal(t, want, stdout.String()) + + stdout.Reset() + cli.Stdin = bytes.NewBuffer([]byte(`{ + "put": "id:ns:type::doc1", + "fields": {"foo": "123"} +}`)) + httpClient.NextResponseString(200, `{"message":"OK"}`) + require.Nil(t, cli.Run("feed", "-")) + assert.Equal(t, want, stdout.String()) } diff --git a/client/go/internal/cli/cmd/root.go b/client/go/internal/cli/cmd/root.go index 58e940d59ef..360af9d0dcf 100644 --- a/client/go/internal/cli/cmd/root.go +++ b/client/go/internal/cli/cmd/root.go @@ -366,7 +366,7 @@ func (c *CLI) createCloudTarget(targetType string, opts targetOptions) (vespa.Ta return nil, errHint(err, "Deployment to cloud requires a certificate. Try 'vespa auth cert'") } deploymentTLSOptions = vespa.TLSOptions{ - KeyPair: &kp.KeyPair, + KeyPair: []tls.Certificate{kp.KeyPair}, CertificateFile: kp.CertificateFile, PrivateKeyFile: kp.PrivateKeyFile, } @@ -377,7 +377,7 @@ func (c *CLI) createCloudTarget(targetType string, opts targetOptions) (vespa.Ta return nil, errHint(err, "Deployment to hosted requires an Athenz certificate", "Try renewing certificate with 'athenz-user-cert'") } apiTLSOptions = vespa.TLSOptions{ - KeyPair: &kp.KeyPair, + KeyPair: []tls.Certificate{kp.KeyPair}, CertificateFile: kp.CertificateFile, PrivateKeyFile: kp.PrivateKeyFile, } diff --git a/client/go/internal/cli/cmd/test.go b/client/go/internal/cli/cmd/test.go index 4a53fe6bed3..05633b1135e 100644 --- a/client/go/internal/cli/cmd/test.go +++ b/client/go/internal/cli/cmd/test.go @@ -263,7 +263,7 @@ func verify(step step, defaultCluster string, defaultParameters map[string]strin var response *http.Response if externalEndpoint { - util.SetCertificate(context.cli.httpClient, []tls.Certificate{}) + util.SetCertificates(context.cli.httpClient, []tls.Certificate{}) response, err = context.cli.httpClient.Do(request, 60*time.Second) } else { response, err = service.Do(request, 600*time.Second) // Vespa should provide a response within the given request timeout diff --git a/client/go/internal/cli/cmd/visit_test.go b/client/go/internal/cli/cmd/visit_test.go index 4302680b9d9..b6e5b893e0b 100644 --- a/client/go/internal/cli/cmd/visit_test.go +++ b/client/go/internal/cli/cmd/visit_test.go @@ -47,7 +47,7 @@ func TestQuoteFunc(t *testing.T) { res := quoteArgForUrl(s) if i < 32 || i > 127 { assert.Equal(t, "a+z", res) - } else { + } else if testing.Verbose() { // go test -v fmt.Printf("res %3d => '%s'\n", i, res) } } diff --git a/client/go/internal/mock/http.go b/client/go/internal/mock/http.go index 9c55f2e79bf..58614d7e5bd 100644 --- a/client/go/internal/mock/http.go +++ b/client/go/internal/mock/http.go @@ -6,6 +6,8 @@ import ( "net/http" "strconv" "time" + + "github.com/vespa-engine/vespa/client/go/internal/util" ) type HTTPClient struct { @@ -58,3 +60,5 @@ func (c *HTTPClient) Do(request *http.Request, timeout time.Duration) (*http.Res }, nil } + +func (c *HTTPClient) Clone() util.HTTPClient { return c } diff --git a/client/go/internal/util/http.go b/client/go/internal/util/http.go index cb35932c8e7..dcf05ed3a14 100644 --- a/client/go/internal/util/http.go +++ b/client/go/internal/util/http.go @@ -2,9 +2,10 @@ package util import ( - "bytes" + "context" "crypto/tls" "fmt" + "net" "net/http" "time" @@ -14,6 +15,7 @@ import ( type HTTPClient interface { Do(request *http.Request, timeout time.Duration) (response *http.Response, error error) + Clone() HTTPClient } type defaultHTTPClient struct { @@ -31,50 +33,57 @@ func (c *defaultHTTPClient) Do(request *http.Request, timeout time.Duration) (re return c.client.Do(request) } -func SetCertificate(client HTTPClient, certificates []tls.Certificate) { +func (c *defaultHTTPClient) Clone() HTTPClient { return CreateClient(c.client.Timeout) } + +func SetCertificates(client HTTPClient, certificates []tls.Certificate) { c, ok := client.(*defaultHTTPClient) if !ok { return } - // Use HTTP/2 transport explicitly. Connection reuse does not work properly when using regular http.Transport, even - // though it upgrades to HTTP/2 automatically - // https://github.com/golang/go/issues/16582 - // https://github.com/golang/go/issues/22091 - var transport *http2.Transport - if _, ok := c.client.Transport.(*http.Transport); ok { - transport = &http2.Transport{} - c.client.Transport = transport - } else if t, ok := c.client.Transport.(*http2.Transport); ok { - transport = t - } else { - panic(fmt.Sprintf("unknown transport type: %T", c.client.Transport)) - } - if ok && !c.hasCertificates(transport.TLSClientConfig, certificates) { - transport.TLSClientConfig = &tls.Config{ + var tlsConfig *tls.Config = nil + if certificates != nil { + tlsConfig = &tls.Config{ Certificates: certificates, MinVersion: tls.VersionTLS12, } } + if tr, ok := c.client.Transport.(*http.Transport); ok { + tr.TLSClientConfig = tlsConfig + } else if tr, ok := c.client.Transport.(*http2.Transport); ok { + tr.TLSClientConfig = tlsConfig + } else { + panic(fmt.Sprintf("unknown transport type: %T", c.client.Transport)) + } } -func (c *defaultHTTPClient) hasCertificates(tlsConfig *tls.Config, certs []tls.Certificate) bool { - if tlsConfig == nil { - return false - } - if len(tlsConfig.Certificates) != len(certs) { - return false +func ForceHTTP2(client HTTPClient, certificates []tls.Certificate) { + c, ok := client.(*defaultHTTPClient) + if !ok { + return } - for i := 0; i < len(certs); i++ { - if len(tlsConfig.Certificates[i].Certificate) != len(certs[i].Certificate) { - return false + var tlsConfig *tls.Config = nil + var dialFunc func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) + if certificates != nil { + tlsConfig = &tls.Config{ + Certificates: certificates, + MinVersion: tls.VersionTLS12, } - for j := 0; j < len(certs[i].Certificate); j++ { - if !bytes.Equal(tlsConfig.Certificates[i].Certificate[j], certs[i].Certificate[j]) { - return false - } + } else { + // No certificate, so force H2C (HTTP/2 over clear-text) by using a non-TLS Dialer + dialer := net.Dialer{} + dialFunc = func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { + return dialer.DialContext(ctx, network, addr) } } - return true + // Use HTTP/2 transport explicitly. Connection reuse does not work properly when using regular http.Transport, even + // though it upgrades to HTTP/2 automatically + // https://github.com/golang/go/issues/16582 + // https://github.com/golang/go/issues/22091 + c.client.Transport = &http2.Transport{ + AllowHTTP: true, + TLSClientConfig: tlsConfig, + DialTLSContext: dialFunc, + } } func CreateClient(timeout time.Duration) HTTPClient { diff --git a/client/go/internal/vespa/document/dispatcher.go b/client/go/internal/vespa/document/dispatcher.go index 7011ae7a9b6..838a7bc45ee 100644 --- a/client/go/internal/vespa/document/dispatcher.go +++ b/client/go/internal/vespa/document/dispatcher.go @@ -1,7 +1,9 @@ package document import ( + "container/list" "fmt" + "io" "sync" "sync/atomic" "time" @@ -16,64 +18,70 @@ type Dispatcher struct { circuitBreaker CircuitBreaker stats Stats - closed bool + started bool ready chan Id results chan Result inflight map[string]*documentGroup inflightCount int64 + errWriter io.Writer mu sync.RWMutex wg sync.WaitGroup resultWg sync.WaitGroup } -// documentGroup holds document operations which share their ID, and must be dispatched in order. -type documentGroup struct { - id Id - operations []documentOp - mu sync.Mutex -} - +// documentOp represents a document operation and the number of times it has been attempted. type documentOp struct { document Document attempts int } -func (g *documentGroup) append(op documentOp) { +// documentGroup holds document operations which share an ID, and must be dispatched in order. +type documentGroup struct { + ops *list.List + mu sync.Mutex +} + +func (g *documentGroup) add(op documentOp, first bool) { g.mu.Lock() defer g.mu.Unlock() - g.operations = append(g.operations, op) + if g.ops == nil { + g.ops = list.New() + } + if first { + g.ops.PushFront(op) + } else { + g.ops.PushBack(op) + } } -func NewDispatcher(feeder Feeder, throttler Throttler, breaker CircuitBreaker) *Dispatcher { +func NewDispatcher(feeder Feeder, throttler Throttler, breaker CircuitBreaker, errWriter io.Writer) *Dispatcher { d := &Dispatcher{ feeder: feeder, throttler: throttler, circuitBreaker: breaker, inflight: make(map[string]*documentGroup), + errWriter: errWriter, } d.start() return d } -func (d *Dispatcher) dispatchAll(g *documentGroup) { - g.mu.Lock() - defer g.mu.Unlock() - for i := 0; i < len(g.operations); i++ { - op := g.operations[i] - ok := false - for !ok { - op.attempts++ - result := d.feeder.Send(op.document) - d.results <- result - ok = result.Status.Success() - if !d.shouldRetry(op, result) { - break - } - } - d.releaseSlot() +func (d *Dispatcher) sendDocumentIn(group *documentGroup) { + group.mu.Lock() + defer group.mu.Unlock() + defer d.releaseSlot() + first := group.ops.Front() + if first == nil { + panic("sending from empty document group, this should not happen") + } + op := group.ops.Remove(first).(documentOp) + op.attempts++ + result := d.feeder.Send(op.document) + d.results <- result + if d.shouldRetry(op, result) { + d.enqueue(op) } - g.operations = nil } func (d *Dispatcher) shouldRetry(op documentOp, result Result) bool { @@ -83,12 +91,26 @@ func (d *Dispatcher) shouldRetry(op documentOp, result Result) bool { return false } if result.HTTPStatus == 429 || result.HTTPStatus == 503 { + fmt.Fprintf(d.errWriter, "feed: %s was throttled with status %d: retrying\n", op.document, result.HTTPStatus) d.throttler.Throttled(atomic.LoadInt64(&d.inflightCount)) return true } - if result.HTTPStatus == 500 || result.HTTPStatus == 502 || result.HTTPStatus == 504 { + if result.Err != nil || result.HTTPStatus == 500 || result.HTTPStatus == 502 || result.HTTPStatus == 504 { + retry := op.attempts <= maxAttempts + msg := "feed: " + op.document.String() + " failed with " + if result.Err != nil { + msg += "error " + result.Err.Error() + } else { + msg += fmt.Sprintf("status %d", result.HTTPStatus) + } + if retry { + msg += ": retrying" + } else { + msg += fmt.Sprintf(": giving up after %d attempts", maxAttempts) + } + fmt.Fprintln(d.errWriter, msg) d.circuitBreaker.Error(fmt.Errorf("request failed with status %d", result.HTTPStatus)) - if op.attempts <= maxAttempts { + if retry { return true } } @@ -98,9 +120,12 @@ func (d *Dispatcher) shouldRetry(op documentOp, result Result) bool { func (d *Dispatcher) start() { d.mu.Lock() defer d.mu.Unlock() + if d.started { + return + } d.ready = make(chan Id, 4096) d.results = make(chan Result, 4096) - d.closed = false + d.started = true d.wg.Add(1) go func() { defer d.wg.Done() @@ -118,13 +143,11 @@ func (d *Dispatcher) readDocuments() { d.mu.RLock() group := d.inflight[id.String()] d.mu.RUnlock() - if group != nil { - d.wg.Add(1) - go func() { - defer d.wg.Done() - d.dispatchAll(group) - }() - } + d.wg.Add(1) + go func() { + defer d.wg.Done() + d.sendDocumentIn(group) + }() } } @@ -134,28 +157,22 @@ func (d *Dispatcher) readResults() { } } -func (d *Dispatcher) Enqueue(doc Document) error { +func (d *Dispatcher) enqueue(op documentOp) error { d.mu.Lock() - if d.closed { + if !d.started { return fmt.Errorf("dispatcher is closed") } - group, ok := d.inflight[doc.Id.String()] - if ok { - group.append(documentOp{document: doc}) - } else { - group = &documentGroup{ - id: doc.Id, - operations: []documentOp{{document: doc}}, - } - d.inflight[doc.Id.String()] = group + group, ok := d.inflight[op.document.Id.String()] + if !ok { + group = &documentGroup{} + d.inflight[op.document.Id.String()] = group } d.mu.Unlock() - d.enqueueWithSlot(doc.Id) + group.add(op, op.attempts > 0) + d.enqueueWithSlot(op.document.Id) return nil } -func (d *Dispatcher) Stats() Stats { return d.stats } - func (d *Dispatcher) enqueueWithSlot(id Id) { d.acquireSlot() d.ready <- id @@ -173,16 +190,20 @@ func (d *Dispatcher) releaseSlot() { atomic.AddInt64(&d.inflightCount, -1) } func closeAndWait[T any](ch chan T, wg *sync.WaitGroup, d *Dispatcher, markClosed bool) { d.mu.Lock() - if !d.closed { + if d.started { close(ch) if markClosed { - d.closed = true + d.started = false } } d.mu.Unlock() wg.Wait() } +func (d *Dispatcher) Enqueue(doc Document) error { return d.enqueue(documentOp{document: doc}) } + +func (d *Dispatcher) Stats() Stats { return d.stats } + // Close closes the dispatcher and waits for all inflight operations to complete. func (d *Dispatcher) Close() error { closeAndWait(d.ready, &d.wg, d, false) diff --git a/client/go/internal/vespa/document/dispatcher_test.go b/client/go/internal/vespa/document/dispatcher_test.go index 8a6d8c6117c..80bc5f603ae 100644 --- a/client/go/internal/vespa/document/dispatcher_test.go +++ b/client/go/internal/vespa/document/dispatcher_test.go @@ -1,6 +1,7 @@ package document import ( + "io" "sync" "testing" "time" @@ -29,7 +30,7 @@ func (f *mockFeeder) Send(doc Document) Result { } else { f.documents = append(f.documents, doc) } - if !result.Status.Success() { + if !result.Success() { result.Stats.Errors = 1 } return result @@ -38,9 +39,9 @@ func (f *mockFeeder) Send(doc Document) Result { func TestDispatcher(t *testing.T) { feeder := &mockFeeder{} clock := &manualClock{tick: time.Second} - throttler := newThrottler(clock.now) + throttler := newThrottler(8, clock.now) breaker := NewCircuitBreaker(time.Second, 0) - dispatcher := NewDispatcher(feeder, throttler, breaker) + dispatcher := NewDispatcher(feeder, throttler, breaker, io.Discard) docs := []Document{ {Id: mustParseId("id:ns:type::doc1"), Operation: OperationPut, Body: []byte(`{"fields":{"foo": "123"}}`)}, {Id: mustParseId("id:ns:type::doc2"), Operation: OperationPut, Body: []byte(`{"fields":{"bar": "456"}}`)}, @@ -71,9 +72,9 @@ func TestDispatcherOrdering(t *testing.T) { {Id: mustParseId("id:ns:type::doc9"), Operation: OperationPut}, } clock := &manualClock{tick: time.Second} - throttler := newThrottler(clock.now) + throttler := newThrottler(8, clock.now) breaker := NewCircuitBreaker(time.Second, 0) - dispatcher := NewDispatcher(feeder, throttler, breaker) + dispatcher := NewDispatcher(feeder, throttler, breaker, io.Discard) for _, d := range docs { dispatcher.Enqueue(d) } @@ -107,9 +108,9 @@ func TestDispatcherOrderingWithFailures(t *testing.T) { } feeder.failAfterN(2) clock := &manualClock{tick: time.Second} - throttler := newThrottler(clock.now) + throttler := newThrottler(8, clock.now) breaker := NewCircuitBreaker(time.Second, 0) - dispatcher := NewDispatcher(feeder, throttler, breaker) + dispatcher := NewDispatcher(feeder, throttler, breaker, io.Discard) for _, d := range docs { dispatcher.Enqueue(d) } diff --git a/client/go/internal/vespa/document/document.go b/client/go/internal/vespa/document/document.go index 98cb2d1b6c6..efb60ad8c0a 100644 --- a/client/go/internal/vespa/document/document.go +++ b/client/go/internal/vespa/document/document.go @@ -130,6 +130,27 @@ type Decoder struct { jsonl bool } +func (d Document) String() string { + var sb strings.Builder + switch d.Operation { + case OperationPut: + sb.WriteString("put ") + case OperationUpdate: + sb.WriteString("update ") + case OperationRemove: + sb.WriteString("remove ") + } + sb.WriteString(d.Id.String()) + if d.Condition != "" { + sb.WriteString(", condition=") + sb.WriteString(d.Condition) + } + if d.Create { + sb.WriteString(", create=true") + } + return sb.String() +} + func (d *Decoder) guessMode() error { for !d.array && !d.jsonl { b, err := d.buf.ReadByte() diff --git a/client/go/internal/vespa/document/feeder.go b/client/go/internal/vespa/document/feeder.go index 8bdd5bca5ba..9e6768d0eb4 100644 --- a/client/go/internal/vespa/document/feeder.go +++ b/client/go/internal/vespa/document/feeder.go @@ -17,8 +17,6 @@ const ( // StatusTransportFailure indicates that there was failure in the transport layer error while sending the document // operation to Vespa. StatusTransportFailure - // StatusError is a catch-all status for any other error that might occur. - StatusError ) // Result represents the result of a feeding operation. @@ -32,8 +30,9 @@ type Result struct { Stats Stats } -// Success returns whether status s is considered a success. -func (s Status) Success() bool { return s == StatusSuccess || s == StatusConditionNotMet } +func (r Result) Success() bool { + return r.Err == nil && (r.Status == StatusSuccess || r.Status == StatusConditionNotMet) +} // Stats represents feeding operation statistics. type Stats struct { diff --git a/client/go/internal/vespa/document/http.go b/client/go/internal/vespa/document/http.go index 4dadcd1d05c..588330a0574 100644 --- a/client/go/internal/vespa/document/http.go +++ b/client/go/internal/vespa/document/http.go @@ -5,10 +5,12 @@ import ( "encoding/json" "fmt" "io" + "math" "net/http" "net/url" "strconv" "strings" + "sync/atomic" "time" "github.com/vespa-engine/vespa/client/go/internal/util" @@ -16,9 +18,10 @@ import ( // Client represents a HTTP client for the /document/v1/ API. type Client struct { - options ClientOptions - httpClient util.HTTPClient - now func() time.Time + options ClientOptions + httpClients []countingHTTPClient + now func() time.Time + sendCount int32 } // ClientOptions specifices the configuration options of a feed client. @@ -29,6 +32,18 @@ type ClientOptions struct { TraceLevel *int } +type countingHTTPClient struct { + client util.HTTPClient + inflight int64 +} + +func (c *countingHTTPClient) addInflight(n int64) { atomic.AddInt64(&c.inflight, n) } + +func (c *countingHTTPClient) Do(req *http.Request, timeout time.Duration) (*http.Response, error) { + defer c.addInflight(-1) + return c.client.Do(req, timeout) +} + type countingReader struct { reader io.Reader bytesRead int64 @@ -40,13 +55,19 @@ func (r *countingReader) Read(p []byte) (int, error) { return n, err } -func NewClient(options ClientOptions, httpClient util.HTTPClient) *Client { - c := &Client{ - options: options, - httpClient: httpClient, - now: time.Now, +func NewClient(options ClientOptions, httpClients []util.HTTPClient) *Client { + if len(httpClients) < 1 { + panic("need at least one HTTP client") + } + countingClients := make([]countingHTTPClient, 0, len(httpClients)) + for _, client := range httpClients { + countingClients = append(countingClients, countingHTTPClient{client: client}) + } + return &Client{ + options: options, + httpClients: countingClients, + now: time.Now, } - return c } func (c *Client) queryParams() url.Values { @@ -109,45 +130,56 @@ func (c *Client) feedURL(d Document, queryParams url.Values) (string, *url.URL, return httpMethod, u, nil } -// Send given document the URL configured in this client. +func (c *Client) leastBusyClient() *countingHTTPClient { + leastBusy := c.httpClients[0] + min := int64(math.MaxInt64) + next := atomic.AddInt32(&c.sendCount, 1) + start := int(next) % len(c.httpClients) + for i := range c.httpClients { + j := (i + start) % len(c.httpClients) + client := c.httpClients[j] + inflight := atomic.LoadInt64(&client.inflight) + if inflight < min { + leastBusy = client + min = inflight + } + } + leastBusy.addInflight(1) + return &leastBusy +} + +// Send given document to the endpoint configured in this client. func (c *Client) Send(document Document) Result { start := c.now() - result := Result{Id: document.Id} - result.Stats.Requests = 1 + result := Result{Id: document.Id, Stats: Stats{Requests: 1}} method, url, err := c.feedURL(document, c.queryParams()) if err != nil { - result.Stats.Errors = 1 - result.Err = err - return result + return resultWithErr(result, err) } req, err := http.NewRequest(method, url.String(), bytes.NewReader(document.Body)) if err != nil { - result.Stats.Errors = 1 - result.Status = StatusError - result.Err = err - return result + return resultWithErr(result, err) } - resp, err := c.httpClient.Do(req, 190*time.Second) + resp, err := c.leastBusyClient().Do(req, 190*time.Second) if err != nil { - result.Stats.Errors = 1 - result.Status = StatusTransportFailure - result.Err = err - return result + return resultWithErr(result, err) } defer resp.Body.Close() - result.Stats.Responses = 1 - result.Stats.ResponsesByCode = map[int]int64{ - resp.StatusCode: 1, - } - result.Stats.BytesSent = int64(len(document.Body)) elapsed := c.now().Sub(start) - result.Stats.TotalLatency = elapsed - result.Stats.MinLatency = elapsed - result.Stats.MaxLatency = elapsed - return c.resultWithResponse(resp, result) + return c.resultWithResponse(resp, result, document, elapsed) +} + +func resultWithErr(result Result, err error) Result { + result.Stats.Errors++ + result.Status = StatusTransportFailure + result.Err = err + return result } -func (c *Client) resultWithResponse(resp *http.Response, result Result) Result { +func (c *Client) resultWithResponse(resp *http.Response, result Result, document Document, elapsed time.Duration) Result { + result.HTTPStatus = resp.StatusCode + result.Stats.Responses++ + result.Stats.ResponsesByCode = map[int]int64{resp.StatusCode: 1} switch resp.StatusCode { case 200: result.Status = StatusSuccess @@ -165,14 +197,18 @@ func (c *Client) resultWithResponse(resp *http.Response, result Result) Result { cr := countingReader{reader: resp.Body} jsonDec := json.NewDecoder(&cr) if err := jsonDec.Decode(&body); err != nil { - result.Status = StatusError + result.Status = StatusVespaFailure result.Err = fmt.Errorf("failed to decode json response: %w", err) } result.Message = body.Message result.Trace = string(body.Trace) + result.Stats.BytesSent = int64(len(document.Body)) result.Stats.BytesRecv = cr.bytesRead - if !result.Status.Success() { - result.Stats.Errors = 1 + if !result.Success() { + result.Stats.Errors++ } + result.Stats.TotalLatency = elapsed + result.Stats.MinLatency = elapsed + result.Stats.MaxLatency = elapsed return result } diff --git a/client/go/internal/vespa/document/http_test.go b/client/go/internal/vespa/document/http_test.go index 311668fa16e..43eaf1bfdf9 100644 --- a/client/go/internal/vespa/document/http_test.go +++ b/client/go/internal/vespa/document/http_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/vespa-engine/vespa/client/go/internal/mock" + "github.com/vespa-engine/vespa/client/go/internal/util" ) type manualClock struct { @@ -25,6 +26,37 @@ func (c *manualClock) now() time.Time { func (c *manualClock) advance(d time.Duration) { c.t = c.t.Add(d) } +type mockHTTPClient struct { + id int + *mock.HTTPClient +} + +func TestLeastBusyClient(t *testing.T) { + httpClient := mock.HTTPClient{} + var httpClients []util.HTTPClient + for i := 0; i < 4; i++ { + httpClients = append(httpClients, &mockHTTPClient{i, &httpClient}) + } + client := NewClient(ClientOptions{}, httpClients) + client.httpClients[0].addInflight(1) + client.httpClients[1].addInflight(1) + assertLeastBusy(t, 2, client) + assertLeastBusy(t, 2, client) + assertLeastBusy(t, 3, client) + client.httpClients[3].addInflight(1) + client.httpClients[1].addInflight(-1) + assertLeastBusy(t, 1, client) +} + +func assertLeastBusy(t *testing.T, id int, client *Client) { + t.Helper() + leastBusy := client.leastBusyClient() + got := leastBusy.client.(*mockHTTPClient).id + if got != id { + t.Errorf("got client.id=%d, want %d", got, id) + } +} + func TestClientSend(t *testing.T) { docs := []Document{ {Create: true, Id: mustParseId("id:ns:type::doc1"), Operation: OperationUpdate, Body: []byte(`{"fields":{"foo": "123"}}`)}, @@ -35,21 +67,43 @@ func TestClientSend(t *testing.T) { client := NewClient(ClientOptions{ BaseURL: "https://example.com:1337", Timeout: time.Duration(5 * time.Second), - }, &httpClient) + }, []util.HTTPClient{&httpClient}) clock := manualClock{t: time.Now(), tick: time.Second} client.now = clock.now var stats Stats for i, doc := range docs { + wantRes := Result{ + Id: doc.Id, + Stats: Stats{ + Requests: 1, + Responses: 1, + TotalLatency: time.Second, + MinLatency: time.Second, + MaxLatency: time.Second, + BytesSent: 25, + }, + } if i < 2 { httpClient.NextResponseString(200, `{"message":"All good!"}`) + wantRes.Status = StatusSuccess + wantRes.HTTPStatus = 200 + wantRes.Message = "All good!" + wantRes.Stats.ResponsesByCode = map[int]int64{200: 1} + wantRes.Stats.BytesRecv = 23 } else { httpClient.NextResponseString(502, `{"message":"Good bye, cruel world!"}`) + wantRes.Status = StatusVespaFailure + wantRes.HTTPStatus = 502 + wantRes.Message = "Good bye, cruel world!" + wantRes.Stats.ResponsesByCode = map[int]int64{502: 1} + wantRes.Stats.Errors = 1 + wantRes.Stats.BytesRecv = 36 } res := client.Send(doc) - stats.Add(res.Stats) - if res.Err != nil { - t.Fatalf("got unexpected error %q", res.Err) + if !reflect.DeepEqual(res, wantRes) { + t.Fatalf("got result %+v, want %+v", res, wantRes) } + stats.Add(res.Stats) r := httpClient.LastRequest if r.Method != http.MethodPut { t.Errorf("got r.Method = %q, want %q", r.Method, http.MethodPut) @@ -176,7 +230,7 @@ func TestClientFeedURL(t *testing.T) { httpClient := mock.HTTPClient{} client := NewClient(ClientOptions{ BaseURL: "https://example.com", - }, &httpClient) + }, []util.HTTPClient{&httpClient}) for i, tt := range tests { moreParams := url.Values{} moreParams.Set("foo", "ba/r") diff --git a/client/go/internal/vespa/document/throttler.go b/client/go/internal/vespa/document/throttler.go index 69bb7c8d7ac..5b0aab6174e 100644 --- a/client/go/internal/vespa/document/throttler.go +++ b/client/go/internal/vespa/document/throttler.go @@ -7,13 +7,7 @@ import ( "time" ) -const ( - throttlerWeight = 0.7 - // TODO(mpolden): Multiply this by connections per endpoint, and number of endpoints when this becomes configurable - // for local target - throttlerMinInflight = 16 - throttlerMaxInflight = 256 * throttlerMinInflight // 4096 max streams per connection on the server side -) +const throttlerWeight = 0.7 type Throttler interface { // Sent notifies the the throttler that a document has been sent. @@ -27,29 +21,38 @@ type Throttler interface { } type dynamicThrottler struct { - ok int64 + minInflight int64 + maxInflight int64 targetInflight int64 targetTimesTen int64 throughputs []float64 + ok int64 sent int64 start time.Time now func() time.Time } -func newThrottler(nowFunc func() time.Time) *dynamicThrottler { +func newThrottler(connections int, nowFunc func() time.Time) *dynamicThrottler { + var ( + minInflight = 16 * int64(connections) + maxInflight = 256 * minInflight // 4096 max streams per connection on the server side + ) return &dynamicThrottler{ + minInflight: minInflight, + maxInflight: maxInflight, + targetInflight: 8 * minInflight, + targetTimesTen: 10 * maxInflight, + throughputs: make([]float64, 128), - start: nowFunc(), - now: nowFunc, - targetInflight: 8 * throttlerMinInflight, - targetTimesTen: 10 * throttlerMaxInflight, + start: nowFunc(), + now: nowFunc, } } -func NewThrottler() Throttler { return newThrottler(time.Now) } +func NewThrottler(connections int) Throttler { return newThrottler(connections, time.Now) } func (t *dynamicThrottler) Sent() { currentInflight := atomic.LoadInt64(&t.targetInflight) @@ -64,7 +67,7 @@ func (t *dynamicThrottler) Sent() { currentThroughput := float64(atomic.SwapInt64(&t.ok, 0)) / float64(elapsed) // Use buckets for throughput over inflight, along the log-scale, in [minInflight, maxInflight). - index := int(float64(len(t.throughputs)) * math.Log(max(1, min(255, float64(currentInflight)/throttlerMinInflight))) / math.Log(256)) + index := int(float64(len(t.throughputs)) * math.Log(max(1, min(255, float64(currentInflight)/float64(t.minInflight)))) / math.Log(256)) t.throughputs[index] = currentThroughput // Loop over throughput measurements and pick the one which optimises throughput and latency. @@ -74,7 +77,7 @@ func (t *dynamicThrottler) Sent() { if t.throughputs[i] == 0 { continue // Skip unknown values } - inflight := float64(throttlerMinInflight) * math.Pow(256, (float64(i)+0.5)/float64(len(t.throughputs))) + inflight := float64(t.minInflight) * math.Pow(256, (float64(i)+0.5)/float64(len(t.throughputs))) objective := t.throughputs[i] * math.Pow(inflight, throttlerWeight-1) // Optimise throughput (weight), but also latency (1 - weight) if objective > maxObjective { maxObjective = objective @@ -82,7 +85,7 @@ func (t *dynamicThrottler) Sent() { } } target := int64((rand.Float64()*0.20 + 0.92) * choice) // Random walk, skewed towards increase - atomic.StoreInt64(&t.targetInflight, max(throttlerMinInflight, min(throttlerMaxInflight, target))) + atomic.StoreInt64(&t.targetInflight, max(t.minInflight, min(t.maxInflight, target))) } func (t *dynamicThrottler) Success() { @@ -91,11 +94,11 @@ func (t *dynamicThrottler) Success() { } func (t *dynamicThrottler) Throttled(inflight int64) { - atomic.StoreInt64(&t.targetTimesTen, max(inflight*5, throttlerMinInflight*10)) + atomic.StoreInt64(&t.targetTimesTen, max(inflight*5, t.minInflight*10)) } func (t *dynamicThrottler) TargetInflight() int64 { - staticTargetInflight := min(throttlerMaxInflight, atomic.LoadInt64(&t.targetTimesTen)/10) + staticTargetInflight := min(t.maxInflight, atomic.LoadInt64(&t.targetTimesTen)/10) targetInflight := atomic.LoadInt64(&t.targetInflight) return min(staticTargetInflight, targetInflight) } diff --git a/client/go/internal/vespa/document/throttler_test.go b/client/go/internal/vespa/document/throttler_test.go index 2fd1e73a45a..a22f059207f 100644 --- a/client/go/internal/vespa/document/throttler_test.go +++ b/client/go/internal/vespa/document/throttler_test.go @@ -7,15 +7,15 @@ import ( func TestThrottler(t *testing.T) { clock := &manualClock{tick: time.Second} - tr := newThrottler(clock.now) + tr := newThrottler(8, clock.now) for i := 0; i < 100; i++ { tr.Sent() } - if got, want := tr.TargetInflight(), int64(128); got != want { + if got, want := tr.TargetInflight(), int64(1024); got != want { t.Errorf("got TargetInflight() = %d, but want %d", got, want) } tr.Throttled(5) - if got, want := tr.TargetInflight(), int64(16); got != want { + if got, want := tr.TargetInflight(), int64(128); got != want { t.Errorf("got TargetInflight() = %d, but want %d", got, want) } } diff --git a/client/go/internal/vespa/target.go b/client/go/internal/vespa/target.go index 51861eb12ab..bc936623bcb 100644 --- a/client/go/internal/vespa/target.go +++ b/client/go/internal/vespa/target.go @@ -74,7 +74,7 @@ type Target interface { // TLSOptions configures the client certificate to use for cloud API or service requests. type TLSOptions struct { - KeyPair *tls.Certificate + KeyPair []tls.Certificate CertificateFile string PrivateKeyFile string AthenzDomain string @@ -93,7 +93,7 @@ type LogOptions struct { // Do sends request to this service. Any required authentication happens automatically. func (s *Service) Do(request *http.Request, timeout time.Duration) (*http.Response, error) { if s.TLSOptions.AthenzDomain != "" && s.TLSOptions.KeyPair != nil { - accessToken, err := s.zts.AccessToken(s.TLSOptions.AthenzDomain, *s.TLSOptions.KeyPair) + accessToken, err := s.zts.AccessToken(s.TLSOptions.AthenzDomain, s.TLSOptions.KeyPair[0]) if err != nil { return nil, err } @@ -105,6 +105,8 @@ func (s *Service) Do(request *http.Request, timeout time.Duration) (*http.Respon return s.httpClient.Do(request, timeout) } +func (s *Service) Client() util.HTTPClient { return s.httpClient } + // Wait polls the health check of this service until it succeeds or timeout passes. func (s *Service) Wait(timeout time.Duration) (int, error) { url := s.BaseURL @@ -139,18 +141,18 @@ type requestFunc func() *http.Request // waitForOK queries url and returns its status code. If the url returns a non-200 status code, it is repeatedly queried // until timeout elapses. -func waitForOK(client util.HTTPClient, url string, certificate *tls.Certificate, timeout time.Duration) (int, error) { +func waitForOK(client util.HTTPClient, url string, certificates []tls.Certificate, timeout time.Duration) (int, error) { req, err := http.NewRequest("GET", url, nil) if err != nil { return 0, err } okFunc := func(status int, response []byte) (bool, error) { return isOK(status), nil } - return wait(client, okFunc, func() *http.Request { return req }, certificate, timeout) + return wait(client, okFunc, func() *http.Request { return req }, certificates, timeout) } -func wait(client util.HTTPClient, fn responseFunc, reqFn requestFunc, certificate *tls.Certificate, timeout time.Duration) (int, error) { - if certificate != nil { - util.SetCertificate(client, []tls.Certificate{*certificate}) +func wait(client util.HTTPClient, fn responseFunc, reqFn requestFunc, certificates []tls.Certificate, timeout time.Duration) (int, error) { + if certificates != nil { + util.SetCertificates(client, certificates) } var ( httpErr error diff --git a/client/go/internal/vespa/target_cloud.go b/client/go/internal/vespa/target_cloud.go index 2335d4f3432..1fb3edd78c5 100644 --- a/client/go/internal/vespa/target_cloud.go +++ b/client/go/internal/vespa/target_cloud.go @@ -161,7 +161,7 @@ func (t *cloudTarget) Service(name string, timeout time.Duration, runID int64, c } if service.TLSOptions.KeyPair != nil { - util.SetCertificate(service.httpClient, []tls.Certificate{*service.TLSOptions.KeyPair}) + util.SetCertificates(service.httpClient, service.TLSOptions.KeyPair) } return service, nil } @@ -175,7 +175,7 @@ func (t *cloudTarget) SignRequest(req *http.Request, keyID string) error { return t.addAuth0AccessToken(req) } } else { - if t.apiOptions.TLSOptions.KeyPair.Certificate == nil { + if t.apiOptions.TLSOptions.KeyPair == nil { return fmt.Errorf("system %s requires a certificate for authentication", t.apiOptions.System.Name) } return nil diff --git a/config-model/src/main/java/com/yahoo/schema/OnnxModel.java b/config-model/src/main/java/com/yahoo/schema/OnnxModel.java index 272b668b5fb..90a27d1f036 100644 --- a/config-model/src/main/java/com/yahoo/schema/OnnxModel.java +++ b/config-model/src/main/java/com/yahoo/schema/OnnxModel.java @@ -1,15 +1,17 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.schema; +import com.yahoo.searchlib.rankingexpression.Reference; import com.yahoo.tensor.TensorType; import com.yahoo.vespa.model.ml.OnnxModelInfo; -import com.yahoo.searchlib.rankingexpression.Reference; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; /** * A global ONNX model distributed using file distribution, similar to ranking constants. @@ -21,6 +23,7 @@ public class OnnxModel extends DistributableResource { private OnnxModelInfo modelInfo = null; private final Map<String, String> inputMap = new HashMap<>(); private final Map<String, String> outputMap = new HashMap<>(); + private final Set<String> initializers = new HashSet<>(); private String statelessExecutionMode = null; private Integer statelessInterOpThreads = null; @@ -101,11 +104,13 @@ public class OnnxModel extends DistributableResource { for (String onnxName : modelInfo.getOutputs()) { addOutputNameMapping(onnxName, OnnxModelInfo.asValidIdentifier(onnxName), false); } + initializers.addAll(modelInfo.getInitializers()); this.modelInfo = modelInfo; } public Map<String, String> getInputMap() { return Collections.unmodifiableMap(inputMap); } public Map<String, String> getOutputMap() { return Collections.unmodifiableMap(outputMap); } + public Set<String> getInitializers() { return Set.copyOf(initializers); } public String getDefaultOutput() { return modelInfo != null ? modelInfo.getDefaultOutput() : ""; diff --git a/config-model/src/main/java/com/yahoo/schema/expressiontransforms/InputRecorder.java b/config-model/src/main/java/com/yahoo/schema/expressiontransforms/InputRecorder.java index af072c5b59a..7f578f07fe3 100644 --- a/config-model/src/main/java/com/yahoo/schema/expressiontransforms/InputRecorder.java +++ b/config-model/src/main/java/com/yahoo/schema/expressiontransforms/InputRecorder.java @@ -2,7 +2,6 @@ package com.yahoo.schema.expressiontransforms; import com.yahoo.schema.FeatureNames; -import com.yahoo.schema.RankProfile; import com.yahoo.searchlib.rankingexpression.RankingExpression; import com.yahoo.searchlib.rankingexpression.Reference; import com.yahoo.searchlib.rankingexpression.parser.ParseException; @@ -12,13 +11,12 @@ import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode; import com.yahoo.searchlib.rankingexpression.rule.ReferenceNode; import com.yahoo.searchlib.rankingexpression.rule.TensorFunctionNode; import com.yahoo.searchlib.rankingexpression.transform.ExpressionTransformer; -import com.yahoo.tensor.functions.DynamicTensor; import com.yahoo.tensor.functions.Generate; -import com.yahoo.tensor.functions.Slice; import java.io.StringReader; import java.util.HashSet; import java.util.Set; +import java.util.logging.Logger; /** * Analyzes expression to figure out what inputs it needs @@ -27,6 +25,8 @@ import java.util.Set; */ public class InputRecorder extends ExpressionTransformer<InputRecorderContext> { + private static final Logger log = Logger.getLogger(InputRecorder.class.getName()); + private final Set<String> neededInputs; private final Set<String> handled = new HashSet<>(); @@ -120,7 +120,11 @@ public class InputRecorder extends ExpressionTransformer<InputRecorderContext> { if (model == null) { throw new IllegalArgumentException("missing onnx model: " + arg); } - for (String onnxInput : model.getInputMap().values()) { + model.getInputMap().forEach((onnxName, onnxInput) -> { + if (model.getInitializers().contains(onnxName)) { + log.fine(() -> "For input '%s': skipping name '%s' as it's an initializer".formatted(onnxInput, onnxName)); + return; + } var reader = new StringReader(onnxInput); try { var asExpression = new RankingExpression(reader); @@ -128,7 +132,7 @@ public class InputRecorder extends ExpressionTransformer<InputRecorderContext> { } catch (ParseException e) { throw new IllegalArgumentException("illegal onnx input '" + onnxInput + "': " + e.getMessage()); } - } + }); return; } neededInputs.add(feature.toString()); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidator.java index 0cc52edf3cc..b1eace947cc 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidator.java @@ -2,11 +2,15 @@ package com.yahoo.vespa.model.application.validation.change; import com.yahoo.config.model.api.ConfigChangeAction; +import com.yahoo.config.model.api.ServiceInfo; import com.yahoo.config.model.deploy.DeployState; import com.yahoo.vespa.model.VespaModel; import com.yahoo.config.application.api.ValidationId; +import com.yahoo.vespa.model.container.ApplicationContainer; +import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.content.cluster.ContentCluster; +import java.util.ArrayList; import java.util.List; /** @@ -19,16 +23,23 @@ public class ContentClusterRemovalValidator implements ChangeValidator { @Override public List<ConfigChangeAction> validate(VespaModel current, VespaModel next, DeployState deployState) { + List<ConfigChangeAction> actions = new ArrayList<>(); for (String currentClusterId : current.getContentClusters().keySet()) { ContentCluster nextCluster = next.getContentClusters().get(currentClusterId); - if (nextCluster == null) + if (nextCluster == null) { deployState.validationOverrides().invalid(ValidationId.contentClusterRemoval, - "Content cluster '" + currentClusterId + "' is removed. " + - "This will cause loss of all data in this cluster", - deployState.now()); - } + "Content cluster '" + currentClusterId + "' is removed. " + + "This will cause loss of all data in this cluster", + deployState.now()); - return List.of(); + // If we allow the removal, we must restart all containers to ensure mbus is OK. + for (ApplicationContainerCluster cluster : next.getContainerClusters().values()) { + actions.add(new VespaRestartAction(cluster.id(), + "Content cluster '" + currentClusterId + "' has been removed", + cluster.getContainers().stream().map(ApplicationContainer::getServiceInfo).toList())); + } + } + } + return actions; } - } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/IdentityProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/IdentityProvider.java index 5e8bb85c29d..2b5dadf5512 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/IdentityProvider.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/IdentityProvider.java @@ -51,15 +51,18 @@ public class IdentityProvider extends SimpleComponent implements IdentityConfig. builder.loadBalancerAddress(loadBalancerName.value()); builder.ztsUrl(ztsUrl != null ? ztsUrl.toString() : ""); builder.athenzDnsSuffix(athenzDnsSuffix != null ? athenzDnsSuffix : ""); - builder.nodeIdentityName("vespa.vespa.tenant"); // TODO Move to Oath configmodel amender + builder.nodeIdentityName(configServerDomain() + ".tenant"); // TODO Move to Oath configmodel amender builder.configserverIdentityName(getConfigserverIdentityName()); } // TODO Move to Oath configmodel amender private String getConfigserverIdentityName() { return String.format("%s.provider_%s_%s", - zone.system() == SystemName.main ? "vespa.vespa" : "vespa.vespa.cd", + configServerDomain(), zone.environment().value(), zone.region().value()); } + private String configServerDomain() { + return zone.system() == SystemName.main ? "vespa.vespa" : "vespa.vespa.cd"; + } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/ml/OnnxModelInfo.java b/config-model/src/main/java/com/yahoo/vespa/model/ml/OnnxModelInfo.java index 2742dc59fcd..7c89a349d7d 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/ml/OnnxModelInfo.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/ml/OnnxModelInfo.java @@ -42,13 +42,16 @@ public class OnnxModelInfo { private final Map<String, OnnxTypeInfo> inputs; private final Map<String, OnnxTypeInfo> outputs; private final Map<String, TensorType> vespaTypes = new HashMap<>(); + private final Set<String> initializers; - private OnnxModelInfo(ApplicationPackage app, String path, Map<String, OnnxTypeInfo> inputs, Map<String, OnnxTypeInfo> outputs, String defaultOutput) { + private OnnxModelInfo(ApplicationPackage app, String path, Map<String, OnnxTypeInfo> inputs, + Map<String, OnnxTypeInfo> outputs, Set<String> initializers, String defaultOutput) { this.app = app; this.modelPath = path; this.inputs = Collections.unmodifiableMap(inputs); this.outputs = Collections.unmodifiableMap(outputs); this.defaultOutput = defaultOutput; + this.initializers = Set.copyOf(initializers); } public String getModelPath() { @@ -63,6 +66,8 @@ public class OnnxModelInfo { return outputs.keySet(); } + public Set<String> getInitializers() { return initializers; } + public String getDefaultOutput() { return defaultOutput; } @@ -208,6 +213,14 @@ public class OnnxModelInfo { } g.writeEndArray(); + g.writeArrayFieldStart("initializers"); + for (Onnx.TensorProto initializers : model.getGraph().getInitializerList()) { + g.writeStartObject(); + g.writeStringField("name", initializers.getName()); + g.writeEndObject(); + } + g.writeEndArray(); + g.writeEndObject(); g.close(); return out.toString(); @@ -218,6 +231,7 @@ public class OnnxModelInfo { JsonNode root = m.readTree(json); Map<String, OnnxTypeInfo> inputs = new HashMap<>(); Map<String, OnnxTypeInfo> outputs = new HashMap<>(); + Set<String> initializers = new HashSet<>(); String defaultOutput = ""; String path = null; @@ -233,7 +247,13 @@ public class OnnxModelInfo { if (root.get("outputs").has(0)) { defaultOutput = root.get("outputs").get(0).get("name").textValue(); } - return new OnnxModelInfo(app, path, inputs, outputs, defaultOutput); + var initializerRoot = root.get("initializers"); + if (initializerRoot != null) { + for (JsonNode initializer : initializerRoot) { + initializers.add(initializer.get("name").textValue()); + } + } + return new OnnxModelInfo(app, path, inputs, outputs, initializers, defaultOutput); } static private void onnxTypeToJson(JsonGenerator g, Onnx.ValueInfoProto valueInfo) throws IOException { diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidatorTest.java index 5c360a9343f..65dfce8ff6c 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidatorTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/ContentClusterRemovalValidatorTest.java @@ -1,14 +1,18 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.application.validation.change; +import com.yahoo.collections.Pair; import com.yahoo.config.application.api.ValidationId; import com.yahoo.config.application.api.ValidationOverrides; +import com.yahoo.config.model.api.ConfigChangeAction; import com.yahoo.config.provision.Environment; import com.yahoo.vespa.model.VespaModel; import com.yahoo.vespa.model.application.validation.ValidationTester; import com.yahoo.yolean.Exceptions; import org.junit.jupiter.api.Test; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -36,7 +40,12 @@ public class ContentClusterRemovalValidatorTest { @Test void testOverridingContentRemovalValidation() { VespaModel previous = tester.deploy(null, getServices("contentClusterId"), Environment.prod, null).getFirst(); - tester.deploy(previous, getServices("newContentClusterId"), Environment.prod, removalOverride); // Allowed due to override + var result = tester.deploy(previous, getServices("newContentClusterId"), Environment.prod, removalOverride); // Allowed due to override + assertEquals(result.getFirst().getContainerClusters().values().stream() + .flatMap(cluster -> cluster.getContainers().stream()) + .map(container -> container.getServiceInfo()) + .toList(), + result.getSecond().stream().flatMap(action -> action.getServices().stream()).toList()); } private static String getServices(String contentClusterId) { diff --git a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileReferencesAndDownloadsMaintainer.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileReferencesAndDownloadsMaintainer.java index 5d5775275c3..17025b10568 100644 --- a/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileReferencesAndDownloadsMaintainer.java +++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/filedistribution/FileReferencesAndDownloadsMaintainer.java @@ -23,7 +23,9 @@ import java.util.stream.Collectors; import static java.nio.file.Files.readAttributes; /** - * Deletes file references and url downloads that have not been used for some time + * Deletes file references and url downloads that have not been used for some time. + * See {@link com.yahoo.vespa.config.proxy.filedistribution.RequestTracker} for how we track + * when a file reference or download was last used. * * @author hmusum */ @@ -32,7 +34,7 @@ class FileReferencesAndDownloadsMaintainer implements Runnable { private static final Logger log = Logger.getLogger(FileReferencesAndDownloadsMaintainer.class.getName()); private static final File defaultUrlDownloadDir = UrlDownloadRpcServer.downloadDir; private static final File defaultFileReferencesDownloadDir = FileDownloader.defaultDownloadDirectory; - private static final Duration defaultDurationToKeepFiles = Duration.ofDays(14); + private static final Duration defaultDurationToKeepFiles = Duration.ofDays(21); private static final Duration interval = Duration.ofMinutes(1); private final ScheduledExecutorService executor = diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java index f6988a6b566..955b1bc8f4f 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java @@ -534,7 +534,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye NestedTransaction transaction = new NestedTransaction(); Optional<ApplicationTransaction> applicationTransaction = hostProvisioner.map(provisioner -> provisioner.lock(applicationId)) .map(lock -> new ApplicationTransaction(lock, transaction)); - try (var applicationLock = tenantApplications.lock(applicationId)) { + try (@SuppressWarnings("unused") var applicationLock = tenantApplications.lock(applicationId)) { Optional<Long> activeSession = tenantApplications.activeSessionOf(applicationId); CompletionWaiter waiter; if (activeSession.isPresent()) { @@ -796,7 +796,7 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye NestedTransaction transaction = new NestedTransaction(); Optional<ApplicationTransaction> applicationTransaction = hostProvisioner.map(provisioner -> provisioner.lock(applicationId)) .map(lock -> new ApplicationTransaction(lock, transaction)); - try (var sessionLock = tenant.getApplicationRepo().lock(applicationId)) { + try (@SuppressWarnings("unused") var sessionLock = tenant.getApplicationRepo().lock(applicationId)) { Optional<Session> activeSession = getActiveSession(applicationId); var sessionZooKeeperClient = tenant.getSessionRepository().createSessionZooKeeperClient(session.getSessionId()); CompletionWaiter waiter = sessionZooKeeperClient.createActiveWaiter(); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java index 88e3134ccad..cddcb0f316d 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java @@ -74,7 +74,7 @@ public class TenantApplications implements RequestHandler, HostValidator { private final MetricUpdater tenantMetricUpdater; private final Clock clock; private final TenantFileSystemDirs tenantFileSystemDirs; - private final ConfigserverConfig configserverConfig; + private final String serverId; private final ListFlag<String> incompatibleVersions; public TenantApplications(TenantName tenant, Curator curator, StripedExecutor<TenantName> zkWatcherExecutor, @@ -95,7 +95,7 @@ public class TenantApplications implements RequestHandler, HostValidator { this.hostRegistry = hostRegistry; this.tenantFileSystemDirs = tenantFileSystemDirs; this.clock = clock; - this.configserverConfig = configserverConfig; + this.serverId = configserverConfig.serverId(); this.incompatibleVersions = PermanentFlags.INCOMPATIBLE_VERSIONS.bindTo(flagSource); } @@ -230,7 +230,7 @@ public class TenantApplications implements RequestHandler, HostValidator { */ public void activateApplication(ApplicationSet applicationSet, long activeSessionId) { ApplicationId id = applicationSet.getId(); - try (Lock lock = lock(id)) { + try (@SuppressWarnings("unused") Lock lock = lock(id)) { if ( ! exists(id)) return; // Application was deleted before activation. if (applicationSet.getApplicationGeneration() != activeSessionId) @@ -269,7 +269,7 @@ public class TenantApplications implements RequestHandler, HostValidator { public void removeApplicationsExcept(Set<ApplicationId> applications) { for (ApplicationId activeApplication : applicationMapper.listApplicationIds()) { if ( ! applications.contains(activeApplication)) { - try (var applicationLock = lock(activeApplication)){ + try (@SuppressWarnings("unused") var applicationLock = lock(activeApplication)){ removeApplication(activeApplication); } } @@ -404,11 +404,11 @@ public class TenantApplications implements RequestHandler, HostValidator { public TenantFileSystemDirs getTenantFileSystemDirs() { return tenantFileSystemDirs; } public CompletionWaiter createRemoveApplicationWaiter(ApplicationId applicationId) { - return RemoveApplicationWaiter.createAndInitialize(curator, applicationId, configserverConfig.serverId()); + return RemoveApplicationWaiter.createAndInitialize(curator, applicationId, serverId); } public CompletionWaiter getRemoveApplicationWaiter(ApplicationId applicationId) { - return RemoveApplicationWaiter.create(curator, applicationId, configserverConfig.serverId()); + return RemoveApplicationWaiter.create(curator, applicationId, serverId); } /** diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java index 86c0c90ca12..62a1704b350 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandler.java @@ -97,8 +97,6 @@ public class ApplicationHandler extends HttpHandler { if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/filedistributionstatus")) return filedistributionStatus(applicationId(path), request); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/logs")) return logs(applicationId(path), request); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/metrics/deployment")) return deploymentMetrics(applicationId(path)); - // TODO: Remove when all usage has migrated to .../metrics/searchnode - if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/metrics/proton")) return searchNodeMetrics(applicationId(path)); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/metrics/searchnode")) return searchNodeMetrics(applicationId(path)); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/reindexing")) return getReindexingStatus(applicationId(path)); if (path.matches("/application/v2/tenant/{tenant}/application/{application}/environment/{ignore}/region/{ignore}/instance/{instance}/service/{service}/{hostname}/status/{*}")) return serviceStatusPage(applicationId(path), path.get("service"), path.get("hostname"), path.getRest(), request); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java index 2948b82dd96..22ef6cc2547 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ApplicationPackageMaintainer.java @@ -88,7 +88,7 @@ public class ApplicationPackageMaintainer extends ConfigServerMaintainer { createLocalSessionIfMissing(applicationId, sessionId); } } - return asSuccessFactor(attempts, failures); + return asSuccessFactorDeviation(attempts, failures); } private static FileDownloader createFileDownloader(List<String> otherConfigServersInCluster, diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintainer.java index dad687aae67..ef48c3bdb98 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintainer.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ConfigServerMaintainer.java @@ -46,9 +46,9 @@ public abstract class ConfigServerMaintainer extends Maintainer { } @Override - public void completed(String job, double successFactor, long durationMs) { + public void completed(String job, double successFactorDeviation, long durationMs) { var context = metric.createContext(Map.of("job", job)); - metric.set("maintenance.successFactor", successFactor, context); + metric.set("maintenance.successFactorDeviation", successFactorDeviation, context); metric.set("maintenance.duration", durationMs, context); } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ReindexingMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ReindexingMaintainer.java index a9672455d09..e81c9b6bcbd 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ReindexingMaintainer.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/ReindexingMaintainer.java @@ -77,7 +77,7 @@ public class ReindexingMaintainer extends ConfigServerMaintainer { } }); } - return asSuccessFactor(attempts.get(), failures.get()); + return asSuccessFactorDeviation(attempts.get(), failures.get()); } private Supplier<Long> lazyGeneration(Application application) { diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java index 728f3e8510f..2ad04fdd572 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/TenantApplicationsTest.java @@ -12,8 +12,8 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.TenantName; import com.yahoo.text.Utf8; import com.yahoo.vespa.config.ConfigKey; -import com.yahoo.vespa.config.server.ConfigServerDB; import com.yahoo.vespa.config.server.ConfigActivationListener; +import com.yahoo.vespa.config.server.ConfigServerDB; import com.yahoo.vespa.config.server.ServerCache; import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs; import com.yahoo.vespa.config.server.host.HostRegistry; @@ -26,7 +26,6 @@ import com.yahoo.vespa.config.server.tenant.TestTenantRepository; import com.yahoo.vespa.curator.CompletionTimeoutException; import com.yahoo.vespa.curator.Curator; import com.yahoo.vespa.curator.mock.MockCurator; -import com.yahoo.vespa.curator.mock.MockCuratorFramework; import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.flags.PermanentFlags; import com.yahoo.vespa.model.VespaModel; @@ -37,22 +36,17 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.xml.sax.SAXException; - import java.io.File; import java.io.IOException; import java.time.Clock; import java.time.Duration; import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.IntStream; -import static com.yahoo.vespa.config.server.application.TenantApplications.RemoveApplicationWaiter; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -195,7 +189,6 @@ public class TenantApplicationsTest { public static class MockConfigActivationListener implements ConfigActivationListener { public final AtomicInteger activated = new AtomicInteger(0); final AtomicInteger removed = new AtomicInteger(0); - final Map<String, Collection<String>> tenantHosts = new LinkedHashMap<>(); @Override public void configActivated(ApplicationSet application) { @@ -247,22 +240,22 @@ public class TenantApplicationsTest { @Test public void testRemoveApplication2of3Respond() throws InterruptedException { - Curator curator = new MockCurator3ConfigServers(); - Thread t1 = setupWaiter(curator); - notifyCompletion(curator, 2); + TenantApplications applications = createZKAppRepo(new InMemoryFlagSource()); + Thread t1 = setupWaiter(applications); + notifyCompletion(applications, 2); t1.join(); } @Test public void testRemoveApplicationAllRespond() throws InterruptedException { - Curator curator = new MockCurator3ConfigServers(); - Thread t1 = setupWaiter(curator); - notifyCompletion(curator, 3); + TenantApplications applications = createZKAppRepo(new InMemoryFlagSource()); + Thread t1 = setupWaiter(applications); + notifyCompletion(applications, 3); t1.join(); } - private Thread setupWaiter(Curator curator) { - Curator.CompletionWaiter waiter = RemoveApplicationWaiter.createAndInitialize(curator, createApplicationId(), "cfg1", Duration.ofSeconds(1)); + private Thread setupWaiter(TenantApplications applications) { + Curator.CompletionWaiter waiter = applications.getRemoveApplicationWaiter(createApplicationId()); Thread t1 = new Thread(() -> { try { waiter.awaitCompletion(Duration.ofSeconds(120)); @@ -274,10 +267,10 @@ public class TenantApplicationsTest { return t1; } - private void notifyCompletion(Curator curator, int respondentCount) { + private void notifyCompletion(TenantApplications applications, int respondentCount) { IntStream.range(0, respondentCount) - .forEach(i -> RemoveApplicationWaiter.create(curator, createApplicationId(), "cfg" + i, Duration.ofSeconds(1)) - .notifyCompletion()); + .forEach(i -> applications.createRemoveApplicationWaiter(createApplicationId()) + .notifyCompletion()); } private TenantApplications createZKAppRepo() { @@ -332,12 +325,4 @@ public class TenantApplicationsTest { flagSource); } - private static class MockCurator3ConfigServers extends Curator { - - public MockCurator3ConfigServers() { - super("host1:2181,host2:2181,host3:2181", "host1:2181,host2:2181,host3:2181", (retryPolicy) -> new MockCuratorFramework(true, false)); - } - - } - } diff --git a/container-search/src/main/java/com/yahoo/prelude/IndexFacts.java b/container-search/src/main/java/com/yahoo/prelude/IndexFacts.java index 88a37ea5a02..92ce6abb319 100644 --- a/container-search/src/main/java/com/yahoo/prelude/IndexFacts.java +++ b/container-search/src/main/java/com/yahoo/prelude/IndexFacts.java @@ -6,11 +6,11 @@ import com.yahoo.search.Query; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import static com.yahoo.text.Lowercase.toLowerCase; @@ -32,6 +32,16 @@ public class IndexFacts { private Map<String, List<String>> clusterByDocument; + private static class DocumentTypeListOffset { + public final int offset; + public final SearchDefinition searchDefinition; + + public DocumentTypeListOffset(int offset, SearchDefinition searchDefinition) { + this.offset = offset; + this.searchDefinition = searchDefinition; + } + } + /** A Map of all known search definitions indexed by name */ private Map<String, SearchDefinition> searchDefinitions = new LinkedHashMap<>(); @@ -100,32 +110,34 @@ public class IndexFacts { private boolean isIndexFromDocumentTypes(String indexName, List<String> documentTypes) { if ( ! isInitialized()) return true; - if (documentTypes.isEmpty()) return unionSearchDefinition.getIndex(indexName) != null; + if (documentTypes.isEmpty()) { + return unionSearchDefinition.getIndex(indexName) != null; + } - for (String docName : documentTypes) { - SearchDefinition sd = searchDefinitions.get(docName); - if (sd != null) { - Index index = sd.getIndex(indexName); - if (index != null) return true; + DocumentTypeListOffset sd = chooseSearchDefinition(documentTypes, 0); + while (sd != null) { + Index index = sd.searchDefinition.getIndex(indexName); + if (index != null) { + return true; } + sd = chooseSearchDefinition(documentTypes, sd.offset); } + return false; } private String getCanonicNameFromDocumentTypes(String indexName, List<String> documentTypes) { if (!isInitialized()) return indexName; - String lowerCased = toLowerCase(indexName); if (documentTypes.isEmpty()) { - Index index = unionSearchDefinition.getIndexByLowerCase(lowerCased); + Index index = unionSearchDefinition.getIndexByLowerCase(toLowerCase(indexName)); return index == null ? indexName : index.getName(); } - for (String docName : documentTypes) { - SearchDefinition sd = searchDefinitions.get(docName); - if (sd != null) { - Index index = sd.getIndexByLowerCase(lowerCased); - if (index != null) return index.getName(); - } + DocumentTypeListOffset sd = chooseSearchDefinition(documentTypes, 0); + while (sd != null) { + Index index = sd.searchDefinition.getIndexByLowerCase(toLowerCase(indexName)); + if (index != null) return index.getName(); + sd = chooseSearchDefinition(documentTypes, sd.offset); } return indexName; } @@ -146,12 +158,13 @@ public class IndexFacts { return index; } - for (String docName : documentTypes) { - SearchDefinition sd = searchDefinitions.get(docName); - if (sd != null) { - Index index = sd.getIndex(canonicName); - if (index != null) return index; - } + DocumentTypeListOffset sd = chooseSearchDefinition(documentTypes, 0); + + while (sd != null) { + Index index = sd.searchDefinition.getIndex(canonicName); + + if (index != null) return index; + sd = chooseSearchDefinition(documentTypes, sd.offset); } return Index.nullIndex; } @@ -174,7 +187,7 @@ public class IndexFacts { * Given a search list which is a mixture of document types and cluster * names, and a restrict list which is a list of document types, return a * set of all valid document types for this combination. Most use-cases for - * fetching index settings will involve calling this method with the + * fetching index settings will involve calling this method with the the * incoming query's {@link com.yahoo.search.query.Model#getSources()} and * {@link com.yahoo.search.query.Model#getRestrict()} as input parameters * before calling any other method of this class. @@ -183,20 +196,20 @@ public class IndexFacts { * @param restrict the restrict list for a query * @return a (possibly empty) set of valid document types */ - private Set<String> resolveDocumentTypes(Collection<String> sources, Set<String> restrict, + private Set<String> resolveDocumentTypes(Collection<String> sources, Collection<String> restrict, Set<String> candidateDocumentTypes) { sources = emptyCollectionIfNull(sources); - restrict = emptySetIfNull(restrict); + restrict = emptyCollectionIfNull(restrict); if (sources.isEmpty()) { if ( ! restrict.isEmpty()) { - return Set.copyOf(restrict); + return new TreeSet<>(restrict); } else { return candidateDocumentTypes; } } - Set<String> toSearch = new HashSet<>(); + Set<String> toSearch = new TreeSet<>(); for (String source : sources) { // source: a document type or a cluster containing them List<String> clusterDocTypes = clusters.get(source); if (clusterDocTypes == null) { // source was a document type @@ -222,8 +235,21 @@ public class IndexFacts { private Collection<String> emptyCollectionIfNull(Collection<String> collection) { return collection == null ? List.of() : collection; } - private Set<String> emptySetIfNull(Set<String> collection) { - return collection == null ? Set.of() : collection; + + /** + * Chooses the correct search definition, default if in doubt. + * + * @return the search definition to use + */ + private DocumentTypeListOffset chooseSearchDefinition(List<String> documentTypes, int index) { + while (index < documentTypes.size()) { + String docName = documentTypes.get(index++); + SearchDefinition sd = searchDefinitions.get(docName); + if (sd != null) { + return new DocumentTypeListOffset(index, sd); + } + } + return null; } /** @@ -253,6 +279,10 @@ public class IndexFacts { return frozen; } + private void ensureNotFrozen() { + if (frozen) throw new IllegalStateException("Tried to modify frozen IndexFacts instance."); + } + public String getDefaultPosition(String sdName) { SearchDefinition sd; if (sdName == null) { @@ -270,16 +300,12 @@ public class IndexFacts { return new Session(query); } - public Session newSession() { - return new Session(Set.of(), Set.of()); - } - - public Session newSession(Collection<String> sources, Set<String> restrict) { + public Session newSession(Collection<String> sources, Collection<String> restrict) { return new Session(sources, restrict); } public Session newSession(Collection<String> sources, - Set<String> restrict, + Collection<String> restrict, Set<String> candidateDocumentTypes) { return new Session(sources, restrict, candidateDocumentTypes); } @@ -297,12 +323,12 @@ public class IndexFacts { documentTypes = List.copyOf(resolveDocumentTypes(query)); } - private Session(Collection<String> sources, Set<String> restrict) { + private Session(Collection<String> sources, Collection<String> restrict) { // Assumption: Search definition name equals document name. documentTypes = List.copyOf(resolveDocumentTypes(sources, restrict, searchDefinitions.keySet())); } - private Session(Collection<String> sources, Set<String> restrict, Set<String> candidateDocumentTypes) { + private Session(Collection<String> sources, Collection<String> restrict, Set<String> candidateDocumentTypes) { documentTypes = List.copyOf(resolveDocumentTypes(sources, restrict, candidateDocumentTypes)); } diff --git a/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterSearcher.java b/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterSearcher.java index f0e3e3f3e44..46332d632fe 100644 --- a/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterSearcher.java +++ b/container-search/src/main/java/com/yahoo/prelude/cluster/ClusterSearcher.java @@ -1,8 +1,8 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.prelude.cluster; -import com.yahoo.component.ComponentId; import com.yahoo.component.annotation.Inject; +import com.yahoo.component.ComponentId; import com.yahoo.component.chain.dependencies.After; import com.yahoo.component.provider.ComponentRegistry; import com.yahoo.container.QrSearchersConfig; @@ -28,6 +28,10 @@ import com.yahoo.vespa.streamingvisitors.VdsStreamingSearcher; import com.yahoo.yolean.Exceptions; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.UUID; @@ -53,7 +57,8 @@ public class ClusterSearcher extends Searcher { private final String searchClusterName; - private final SchemaResolver schemaResolver; + // The set of document types contained in this search cluster + private final Set<String> schemas; private final long maxQueryTimeout; // in milliseconds private final long maxQueryCacheTimeout; // in milliseconds @@ -79,7 +84,7 @@ public class ClusterSearcher extends Searcher { searchClusterName = clusterConfig.clusterName(); QrSearchersConfig.Searchcluster searchClusterConfig = getSearchClusterConfigFromClusterName(qrsConfig, searchClusterName); this.globalPhaseRanker = searchClusterConfig.globalphase() ? globalPhaseRanker : null; - this.schemaResolver = new SchemaResolver(documentDbConfig); + schemas = new LinkedHashSet<>(); maxQueryTimeout = ParameterParser.asMilliSeconds(clusterConfig.maxQueryTimeout(), DEFAULT_MAX_QUERY_TIMEOUT); maxQueryCacheTimeout = ParameterParser.asMilliSeconds(clusterConfig.maxQueryCacheTimeout(), DEFAULT_MAX_QUERY_CACHE_TIMEOUT); @@ -88,6 +93,9 @@ public class ClusterSearcher extends Searcher { .com().yahoo().prelude().fastsearch().FastSearcher().docsum() .defaultclass()); + for (DocumentdbInfoConfig.Documentdb docDb : documentDbConfig.documentdb()) + schemas.add(docDb.name()); + String uniqueServerId = UUID.randomUUID().toString(); if (searchClusterConfig.indexingmode() == STREAMING) { server = vdsCluster(uniqueServerId, searchClusterIndex, @@ -149,7 +157,7 @@ public class ClusterSearcher extends Searcher { /** Do not use, for internal testing purposes only. **/ ClusterSearcher(Set<String> schemas, VespaBackEndSearcher searcher, Executor executor) { - this.schemaResolver = new SchemaResolver(schemas); + this.schemas = schemas; searchClusterName = "testScenario"; maxQueryTimeout = DEFAULT_MAX_QUERY_TIMEOUT; maxQueryCacheTimeout = DEFAULT_MAX_QUERY_CACHE_TIMEOUT; @@ -221,9 +229,8 @@ public class ClusterSearcher extends Searcher { } private Result doSearch(Searcher searcher, Query query, Execution execution) { - var schemas = schemaResolver.resolve(query, execution); if (schemas.size() > 1) { - return searchMultipleDocumentTypes(searcher, query, execution, schemas); + return searchMultipleDocumentTypes(searcher, query, execution); } else { String docType = schemas.iterator().next(); query.getModel().setRestrict(docType); @@ -259,7 +266,8 @@ public class ClusterSearcher extends Searcher { } } - private Result searchMultipleDocumentTypes(Searcher searcher, Query query, Execution execution, Set<String> schemas) { + private Result searchMultipleDocumentTypes(Searcher searcher, Query query, Execution execution) { + Set<String> schemas = resolveSchemas(query, execution.context().getIndexFacts()); List<Query> queries = createQueries(query, schemas); if (queries.size() == 1) { return perSchemaSearch(searcher, queries.get(0), execution); @@ -293,7 +301,25 @@ public class ClusterSearcher extends Searcher { } Set<String> resolveSchemas(Query query, IndexFacts indexFacts) { - return schemaResolver.resolve(query, indexFacts); + Set<String> restrict = query.getModel().getRestrict(); + if (restrict == null || restrict.isEmpty()) { + Set<String> sources = query.getModel().getSources(); + return (sources == null || sources.isEmpty()) + ? schemas + : new HashSet<>(indexFacts.newSession(sources, Collections.emptyList(), schemas).documentTypes()); + } else { + return filterValidDocumentTypes(restrict); + } + } + + private Set<String> filterValidDocumentTypes(Collection<String> restrict) { + Set<String> retval = new LinkedHashSet<>(); + for (String docType : restrict) { + if (docType != null && schemas.contains(docType)) { + retval.add(docType); + } + } + return retval; } private List<Query> createQueries(Query query, Set<String> docTypes) { diff --git a/container-search/src/main/java/com/yahoo/prelude/cluster/SchemaResolver.java b/container-search/src/main/java/com/yahoo/prelude/cluster/SchemaResolver.java deleted file mode 100644 index 3a2125d1d38..00000000000 --- a/container-search/src/main/java/com/yahoo/prelude/cluster/SchemaResolver.java +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -package com.yahoo.prelude.cluster; - -import com.yahoo.prelude.IndexFacts; -import com.yahoo.prelude.fastsearch.DocumentdbInfoConfig; -import com.yahoo.search.Query; -import com.yahoo.search.searchchain.Execution; - -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * Resolves schemas from query and execution context - * - * @author bjorncs - */ -class SchemaResolver { - - private final Set<String> schemas; - - SchemaResolver(DocumentdbInfoConfig cfg) { - this(cfg.documentdb().stream().map(DocumentdbInfoConfig.Documentdb::name).toList()); - } - - SchemaResolver(Collection<String> schemas) { - this.schemas = new LinkedHashSet<>(schemas); - } - - Set<String> resolve(Query query, Execution execution) { - return resolve(query, execution.context().getIndexFacts()); - } - - Set<String> resolve(Query query, IndexFacts indexFacts) { - if (schemas.size() == 1) return Set.of(schemas.iterator().next()); - var restrict = query.getModel().getRestrict(); - if (restrict == null || restrict.isEmpty()) { - Set<String> sources = query.getModel().getSources(); - return (sources == null || sources.isEmpty()) - ? schemas - : new LinkedHashSet<>(indexFacts.newSession(sources, Set.of(), schemas).documentTypes()); - } else { - return filterValidDocumentTypes(restrict); - } - } - - private Set<String> filterValidDocumentTypes(Collection<String> restrict) { - Set<String> retval = new LinkedHashSet<>(); - for (String docType : restrict) { - if (docType != null && schemas.contains(docType)) { - retval.add(docType); - } - } - return retval; - } - -} diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/CustomParser.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/CustomParser.java index 2bd408220cd..e3b2278475b 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/parser/CustomParser.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/CustomParser.java @@ -6,6 +6,7 @@ import com.yahoo.prelude.IndexFacts; import com.yahoo.prelude.query.Item; import com.yahoo.search.query.parser.Parser; +import java.util.Collections; import java.util.Set; /** @@ -22,7 +23,7 @@ public interface CustomParser extends Parser { Set<String> toSearch, IndexFacts indexFacts, String defaultIndexName) { if (indexFacts == null) indexFacts = new IndexFacts(); - return parse(queryToParse, filterToParse, parsingLanguage, indexFacts.newSession(toSearch, Set.of()), defaultIndexName); + return parse(queryToParse, filterToParse, parsingLanguage, indexFacts.newSession(toSearch, Collections.emptySet()), defaultIndexName); } Item parse(String queryToParse, String filterToParse, Language parsingLanguage, diff --git a/container-search/src/main/java/com/yahoo/prelude/query/parser/Tokenizer.java b/container-search/src/main/java/com/yahoo/prelude/query/parser/Tokenizer.java index 9952ec64d13..c1d415b8e27 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/parser/Tokenizer.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/parser/Tokenizer.java @@ -8,6 +8,7 @@ import com.yahoo.prelude.Index; import com.yahoo.prelude.IndexFacts; import com.yahoo.prelude.query.Substring; +import java.util.Collections; import java.util.List; import static com.yahoo.prelude.query.parser.Token.Kind.*; @@ -62,7 +63,7 @@ public final class Tokenizer { * @return a read-only list of tokens. This list can only be used by this thread */ public List<Token> tokenize(String string) { - return tokenize(string, new IndexFacts().newSession()); + return tokenize(string, new IndexFacts().newSession(Collections.emptySet(), Collections.emptySet())); } /** @@ -170,10 +171,13 @@ public final class Tokenizer { // this is a heuristic to check whether we probably have reached the end of an URL element for (int i = tokens.size() - 1; i >= 0; --i) { switch (tokens.get(i).kind) { - case COLON -> { if (i == indexLastExplicitlyChangedAt) return false; } - case SPACE -> { return true; } - default -> { } - // do nothing + case COLON: + if (i == indexLastExplicitlyChangedAt) return false; + break; + case SPACE: + return true; + default: + // do nothing } } // really not sure whether we should choose false instead, on cause of the guard at diff --git a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/TokenizerTestCase.java b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/TokenizerTestCase.java index 3a6be1521e2..1ff5574ec03 100644 --- a/container-search/src/test/java/com/yahoo/prelude/query/parser/test/TokenizerTestCase.java +++ b/container-search/src/test/java/com/yahoo/prelude/query/parser/test/TokenizerTestCase.java @@ -13,6 +13,7 @@ import com.yahoo.prelude.query.parser.Tokenizer; import org.junit.jupiter.api.Test; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static com.yahoo.prelude.query.parser.Token.Kind.COLON; @@ -28,9 +29,7 @@ import static com.yahoo.prelude.query.parser.Token.Kind.SPACE; import static com.yahoo.prelude.query.parser.Token.Kind.STAR; import static com.yahoo.prelude.query.parser.Token.Kind.UNDERSCORE; import static com.yahoo.prelude.query.parser.Token.Kind.WORD; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; /** * Tests the tokenizer @@ -284,7 +283,7 @@ public class TokenizerTestCase { sd.addIndex(index2); IndexFacts facts = new IndexFacts(new IndexModel(sd)); - IndexFacts.Session session = facts.newSession(); + IndexFacts.Session session = facts.newSession(Collections.emptySet(), Collections.emptySet()); Tokenizer tokenizer = new Tokenizer(new SimpleLinguistics()); List<?> tokens = tokenizer.tokenize("normal a:b (normal testexact1:/,%#%&+-+ ) testexact2:ho_/&%&/()/aa*::*& b:c", "default", session); // tokenizer.print(); @@ -329,7 +328,7 @@ public class TokenizerTestCase { IndexFacts facts = new IndexFacts(new IndexModel(sd)); Tokenizer tokenizer = new Tokenizer(new SimpleLinguistics()); - IndexFacts.Session session = facts.newSession(); + IndexFacts.Session session = facts.newSession(Collections.emptySet(), Collections.emptySet()); List<?> tokens = tokenizer.tokenize("normal a:b (normal testexact1:/,%#%&+-+ ) testexact2:ho_/&%&/()/aa*::*&", session); assertEquals(new Token(WORD, "normal"), tokens.get(0)); assertEquals(new Token(SPACE, " "), tokens.get(1)); @@ -366,7 +365,7 @@ public class TokenizerTestCase { IndexFacts facts = new IndexFacts(new IndexModel(sd)); Tokenizer tokenizer = new Tokenizer(new SimpleLinguistics()); - IndexFacts.Session session = facts.newSession(); + IndexFacts.Session session = facts.newSession(Collections.emptySet(), Collections.emptySet()); List<?> tokens = tokenizer.tokenize("normal a:b (normal testexact1:/,%#%&+-+ ) testexact2:ho_/&%&/()/aa*::*", session); assertEquals(new Token(WORD, "normal"), tokens.get(0)); assertEquals(new Token(SPACE, " "), tokens.get(1)); @@ -403,7 +402,7 @@ public class TokenizerTestCase { IndexFacts facts = new IndexFacts(new IndexModel(sd)); Tokenizer tokenizer = new Tokenizer(new SimpleLinguistics()); - IndexFacts.Session session = facts.newSession(); + IndexFacts.Session session = facts.newSession(Collections.emptySet(), Collections.emptySet()); List<?> tokens = tokenizer.tokenize("normal a:b (normal testexact1:!/%#%&+-+ ) testexact2:ho_/&%&/()/aa*::*&b:", session); assertEquals(new Token(WORD, "normal"), tokens.get(0)); assertEquals(new Token(SPACE, " "), tokens.get(1)); @@ -440,7 +439,7 @@ public class TokenizerTestCase { sd.addIndex(index2); IndexFacts indexFacts = new IndexFacts(new IndexModel(sd)); - IndexFacts.Session facts = indexFacts.newSession(); + IndexFacts.Session facts = indexFacts.newSession(Collections.emptySet(), Collections.emptySet()); Tokenizer tokenizer = new Tokenizer(new SimpleLinguistics()); List<?> tokens = tokenizer.tokenize("normal a:b (normal testexact1:foo) testexact2:bar", facts); diff --git a/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java b/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java index dbcb393c922..e6c5a18c9da 100644 --- a/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java +++ b/container-search/src/test/java/com/yahoo/prelude/test/IndexFactsTestCase.java @@ -15,12 +15,8 @@ import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Set; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; /** * Tests using synthetic index names for IndexFacts class. @@ -184,7 +180,7 @@ public class IndexFactsTestCase { query.getModel().getSources().add("one"); query.getModel().getRestrict().add("two"); - IndexFacts.Session indexFacts = createIndexFacts().newSession(List.of("clusterOne"), Set.of()); + IndexFacts.Session indexFacts = createIndexFacts().newSession(List.of("clusterOne"), List.of()); assertTrue(indexFacts.isIndex("a")); assertFalse(indexFacts.isIndex("b")); assertTrue(indexFacts.isIndex("d")); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java index 1eb68c14353..80b52e0c7a4 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java @@ -31,7 +31,8 @@ import static java.util.Comparator.comparing; */ public class Endpoint { - private static final String OATH_DNS_SUFFIX = ".vespa.oath.cloud"; + private static final String MAIN_OATH_DNS_SUFFIX = ".vespa.oath.cloud"; + private static final String CD_OATH_DNS_SUFFIX = ".cd.vespa.oath.cloud"; private static final String PUBLIC_DNS_SUFFIX = ".vespa-app.cloud"; private static final String PUBLIC_CD_DNS_SUFFIX = ".cd.vespa-app.cloud"; @@ -243,18 +244,13 @@ public class Endpoint { /** Returns the DNS suffix used for endpoints in given system */ private static String dnsSuffix(SystemName system) { - switch (system) { - case cd, main -> { - return OATH_DNS_SUFFIX; - } - case Public -> { - return PUBLIC_DNS_SUFFIX; - } - case PublicCd -> { - return PUBLIC_CD_DNS_SUFFIX; - } + return switch (system) { + case cd -> CD_OATH_DNS_SUFFIX; + case main -> MAIN_OATH_DNS_SUFFIX; + case Public -> PUBLIC_DNS_SUFFIX; + case PublicCd -> PUBLIC_CD_DNS_SUFFIX; default -> throw new IllegalArgumentException("No DNS suffix declared for system " + system); - } + }; } /** Returns the DNS suffix used for internal names (i.e. names not exposed to tenants) in given system */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLog.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLog.java index aa6e3b0c44d..cbd0f685d80 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLog.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLog.java @@ -49,7 +49,7 @@ public record AuditLog(List<Entry> entries) { public record Entry(Instant at, String principal, Method method, String resource, Optional<String> data, Client client) implements Comparable<Entry> { - private final static int maxDataLength = 1024; + final static int maxDataLength = 1024; private final static Comparator<Entry> comparator = Comparator.comparing(Entry::at).reversed(); public Entry(Instant at, Client client, String principal, Method method, String resource, byte[] data) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLogger.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLogger.java index 033cd0a52c9..13b3d9d170f 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLogger.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/auditlog/AuditLogger.java @@ -4,11 +4,12 @@ package com.yahoo.vespa.hosted.controller.auditlog; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.jdisc.http.HttpHeaders; import com.yahoo.transaction.Mutex; +import com.yahoo.vespa.hosted.controller.auditlog.AuditLog.Entry; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.UncheckedIOException; +import java.io.InputStream; +import java.io.SequenceInputStream; import java.net.URI; import java.security.Principal; import java.time.Clock; @@ -17,6 +18,9 @@ import java.time.Instant; import java.util.Objects; import java.util.Optional; +import static com.yahoo.yolean.Exceptions.uncheck; +import static java.util.Objects.requireNonNullElse; + /** * This provides read and write operations for the audit log. * @@ -58,14 +62,8 @@ public class AuditLogger { "misconfiguration and should not happen"); } - byte[] data = new byte[0]; - try { - if (request.getData() != null) { - data = request.getData().readAllBytes(); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } + InputStream requestData = requireNonNullElse(request.getData(), InputStream.nullInputStream()); + byte[] data = uncheck(() -> requestData.readNBytes(Entry.maxDataLength)); AuditLog.Entry.Client client = parseClient(request); Instant now = clock.instant(); @@ -80,7 +78,9 @@ public class AuditLogger { } // Create a new input stream to allow callers to consume request body - return new HttpRequest(request.getJDiscRequest(), new ByteArrayInputStream(data), request.propertyMap()); + return new HttpRequest(request.getJDiscRequest(), + new SequenceInputStream(new ByteArrayInputStream(data), requestData), + request.propertyMap()); } private static AuditLog.Entry.Client parseClient(HttpRequest request) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java index 050b77a391e..ecd24b3577f 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java @@ -303,7 +303,7 @@ public class DeploymentStatus { fallbackPlatform(change, job)); if (step.completedAt(change, firstProductionJobWithDeploymentInCloud).isEmpty()) { JobType typeWithZone = job.type().isSystemTest() ? JobType.systemTest(zones, cloud) : JobType.stagingTest(zones, cloud); - jobs.merge(job, List.of(new Job(typeWithZone, versions, step.readyAt(change), change)), DeploymentStatus::union); + jobs.merge(job, List.of(new Job(typeWithZone, versions, step.readyAt(change, firstProductionJobWithDeploymentInCloud), change)), DeploymentStatus::union); } }); }); @@ -679,7 +679,7 @@ public class DeploymentStatus { .asList().isEmpty()) testJobs.merge(testJob, List.of(new Job(testJob.type(), productionJob.versions(), - jobSteps().get(testJob).readyAt(productionJob.change), + jobSteps().get(testJob).readyAt(productionJob.change, Optional.of(job)), productionJob.change)), DeploymentStatus::union); }); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java index 02e1818932e..cbdfcf70123 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ApplicationOwnershipConfirmer.java @@ -79,7 +79,7 @@ public class ApplicationOwnershipConfirmer extends ControllerMaintainer { log.log(Level.INFO, "Exception caught when attempting to file an issue for '" + application.id() + "': " + Exceptions.toMessageString(e)); } }); - return asSuccessFactor(attempts.get(), failures.get()); + return asSuccessFactorDeviation(attempts.get(), failures.get()); } private boolean isInCurrentShard(TenantAndApplicationId id) { @@ -122,7 +122,7 @@ public class ApplicationOwnershipConfirmer extends ControllerMaintainer { log.log(Level.INFO, "Exception caught when attempting to escalate issue with id '" + issueId + "': " + Exceptions.toMessageString(e)); } }); - return asSuccessFactor(attempts.get(), failures.get()); + return asSuccessFactorDeviation(attempts.get(), failures.get()); } private double updateConfirmedApplicationOwners() { @@ -149,7 +149,7 @@ public class ApplicationOwnershipConfirmer extends ControllerMaintainer { log.log(Level.INFO, "Exception caught when attempting to find confirmed owner of issue with id '" + issueId + "': " + Exceptions.toMessageString(e)); } }); - return asSuccessFactor(attempts.get(), failures.get()); + return asSuccessFactorDeviation(attempts.get(), failures.get()); } private ApplicationList applications() { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdater.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdater.java index 518027f8099..c4f3c611cc5 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdater.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveUriUpdater.java @@ -98,7 +98,7 @@ public class ArchiveUriUpdater extends ControllerMaintainer { } } - return asSuccessFactor(tenantsByZone.size(), failures); + return asSuccessFactorDeviation(tenantsByZone.size(), failures); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java index 25c4121c271..32d06286820 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArtifactExpirer.java @@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.component.Version; import com.yahoo.config.provision.CloudName; -import com.yahoo.config.provision.SystemName; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.api.integration.artifact.Artifact; import com.yahoo.vespa.hosted.controller.api.integration.artifact.ArtifactRegistry; @@ -13,12 +12,9 @@ import com.yahoo.vespa.hosted.controller.versions.VespaVersion; import java.time.Duration; import java.time.Instant; import java.util.Comparator; -import java.util.EnumSet; import java.util.List; -import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; /** * Periodically expire unused artifacts, e.g. container images and RPMs. @@ -32,7 +28,7 @@ public class ArtifactExpirer extends ControllerMaintainer { private static final Duration MIN_AGE = Duration.ofDays(14); public ArtifactExpirer(Controller controller, Duration interval) { - super(controller, interval, null, expiringSystems()); + super(controller, interval); } @Override @@ -56,10 +52,10 @@ public class ArtifactExpirer extends ControllerMaintainer { log.log(Level.INFO, "Expiring " + artifactsToExpire.size() + " artifacts in " + cloudName + ": " + artifactsToExpire); artifactRegistry.deleteAll(artifactsToExpire); } - return 1; + return 0; } catch (RuntimeException e) { log.log(Level.WARNING, "Failed to expire artifacts in " + cloudName + ". Will retry in " + interval(), e); - return 0; + return 1; } } @@ -77,11 +73,4 @@ public class ArtifactExpirer extends ControllerMaintainer { return true; } - /** Returns systems where artifacts can be expired */ - private static Set<SystemName> expiringSystems() { - // Run only in public and main. Public systems have distinct container registries, while main and CD have - // shared registries. - return EnumSet.of(SystemName.Public, SystemName.PublicCd, SystemName.main); - } - } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BcpGroupUpdater.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BcpGroupUpdater.java index 32e6ad0d557..fe1930a19ea 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BcpGroupUpdater.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BcpGroupUpdater.java @@ -42,11 +42,13 @@ public class BcpGroupUpdater extends ControllerMaintainer { private final ApplicationController applications; private final NodeRepository nodeRepository; + private final Double successFactorBaseline; - public BcpGroupUpdater(Controller controller, Duration duration) { - super(controller, duration); + public BcpGroupUpdater(Controller controller, Duration duration, Double successFactorBaseline) { + super(controller, duration, successFactorBaseline); this.applications = controller.applications(); this.nodeRepository = controller.serviceRegistry().configServer().nodeRepository(); + this.successFactorBaseline = successFactorBaseline; } @Override @@ -58,7 +60,7 @@ public class BcpGroupUpdater extends ControllerMaintainer { for (var application : applications.asList()) { for (var instance : application.instances().values()) { for (var deployment : instance.productionDeployments().values()) { - if (shuttingDown()) return 1.0; + if (shuttingDown()) return 0.0; try { attempts++; var bcpGroups = BcpGroup.groupsFrom(instance, application.deploymentSpec()); @@ -75,12 +77,12 @@ public class BcpGroupUpdater extends ControllerMaintainer { } } } - double successFactor = asSuccessFactor(attempts, failures); - if ( successFactor == 0 ) + double successFactorDeviation = asSuccessFactorDeviation(attempts, failures); + if ( successFactorDeviation == -successFactorBaseline ) log.log(Level.WARNING, "Could not update traffic share on any applications", lastException); - else if ( successFactor < 0.9 ) + else if ( successFactorDeviation < -0.1 ) log.log(Level.FINE, "Could not update traffic share on all applications", lastException); - return successFactor; + return successFactorDeviation; } /** Adds deployment traffic share to the given patch. */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingDatabaseMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingDatabaseMaintainer.java index a7ebaec7c09..b40078eef51 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingDatabaseMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingDatabaseMaintainer.java @@ -19,6 +19,6 @@ public class BillingDatabaseMaintainer extends ControllerMaintainer { @Override protected double maintain() { controller().serviceRegistry().billingDatabase().maintain(); - return 1; + return 0.0; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudDatabaseMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudDatabaseMaintainer.java index 914707aa318..68fd5c8bafe 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudDatabaseMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudDatabaseMaintainer.java @@ -19,8 +19,8 @@ public class CloudDatabaseMaintainer extends ControllerMaintainer { controller().serviceRegistry().billingController().updateCache(tenants); } catch (Exception e) { log.warning("Could not update cloud database cache: " + Exceptions.toMessageString(e)); - return 0.0; + return 1.0; } - return 1.0; + return 0.0; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainer.java index 6ecad482cd2..f9c93a87c44 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ContactInformationMaintainer.java @@ -64,7 +64,7 @@ public class ContactInformationMaintainer extends ControllerMaintainer { interval()); } } - return asSuccessFactor(attempts, failures); + return asSuccessFactorDeviation(attempts, failures); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainer.java index c861d522818..f21803283eb 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainer.java @@ -12,7 +12,6 @@ import java.util.EnumSet; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.logging.Logger; /** * A maintainer is some job which runs at a fixed interval to perform some maintenance task in the controller. @@ -26,13 +25,22 @@ public abstract class ControllerMaintainer extends Maintainer { /** The systems in which this maintainer should run */ private final Set<SystemName> activeSystems; + public ControllerMaintainer(Controller controller, Duration interval) { - this(controller, interval, null, EnumSet.allOf(SystemName.class)); + this(controller, interval, null, EnumSet.allOf(SystemName.class), 1.0); + } + + public ControllerMaintainer(Controller controller, Duration interval, Double successFactorBaseline) { + this(controller, interval, null, EnumSet.allOf(SystemName.class), successFactorBaseline); } public ControllerMaintainer(Controller controller, Duration interval, String name, Set<SystemName> activeSystems) { + this(controller, interval, name, activeSystems, 1.0); + } + + public ControllerMaintainer(Controller controller, Duration interval, String name, Set<SystemName> activeSystems, Double successFactorBaseline) { super(name, interval, controller.clock(), controller.jobControl(), - new ControllerJobMetrics(controller.metric()), controller.curator().cluster(), true); + new ControllerJobMetrics(controller.metric()), controller.curator().cluster(), true, successFactorBaseline); this.controller = controller; this.activeSystems = Set.copyOf(Objects.requireNonNull(activeSystems)); } @@ -54,8 +62,8 @@ public abstract class ControllerMaintainer extends Maintainer { } @Override - public void completed(String job, double successFactor, long durationMs) { - metric.set("maintenance.successFactor", successFactor, metric.createContext(Map.of("job", job))); + public void completed(String job, double successFactorDeviation, long durationMs) { + metric.set("maintenance.successFactorDeviation", successFactorDeviation, metric.createContext(Map.of("job", job))); metric.set("maintenance.duration", durationMs, metric.createContext(Map.of("job", job))); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java index 05159b38ec6..7f48a1115c5 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java @@ -40,6 +40,7 @@ public class ControllerMaintenance extends AbstractComponent { @SuppressWarnings("unused") // instantiated by Dependency Injection public ControllerMaintenance(Controller controller, Metric metric, UserManagement userManagement, AthenzClientFactory athenzClientFactory) { Intervals intervals = new Intervals(controller.system()); + SuccessFactorBaseline successFactorBaseline = new SuccessFactorBaseline(controller.system()); upgrader = new Upgrader(controller, intervals.defaultInterval); osUpgradeScheduler = new OsUpgradeScheduler(controller, intervals.osUpgradeScheduler); maintainers.add(upgrader); @@ -68,7 +69,7 @@ public class ControllerMaintenance extends AbstractComponent { maintainers.add(new HostInfoUpdater(controller, intervals.hostInfoUpdater)); maintainers.add(new ReindexingTriggerer(controller, intervals.reindexingTriggerer)); maintainers.add(new EndpointCertificateMaintainer(controller, intervals.endpointCertificateMaintainer)); - maintainers.add(new BcpGroupUpdater(controller, intervals.trafficFractionUpdater)); + maintainers.add(new BcpGroupUpdater(controller, intervals.trafficFractionUpdater, successFactorBaseline.trafficFractionUpdater)); maintainers.add(new ArchiveUriUpdater(controller, intervals.archiveUriUpdater)); maintainers.add(new ArchiveAccessMaintainer(controller, metric, intervals.archiveAccessMaintainer)); maintainers.add(new TenantRoleMaintainer(controller, intervals.tenantRoleMaintainer)); @@ -189,4 +190,15 @@ public class ControllerMaintenance extends AbstractComponent { } + private static class SuccessFactorBaseline { + + private final Double defaultSuccessFactorBaseline; + private final Double trafficFractionUpdater; + + public SuccessFactorBaseline(SystemName system) { + Objects.requireNonNull(system); + this.defaultSuccessFactorBaseline = 1.0; + this.trafficFractionUpdater = system.isCd() ? 0.5 : 0.7; + } + } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java index 403b5aed1ce..668893d5a7e 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java @@ -34,7 +34,7 @@ public class CostReportMaintainer extends ControllerMaintainer { protected double maintain() { var csv = CostCalculator.resourceShareByPropertyToCsv(nodeRepository, controller(), clock, consumer.fixedAllocations()); consumer.consume(csv); - return 1.0; + return 0.0; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirer.java index 97f3f955a20..c22cb1efdb3 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentExpirer.java @@ -5,8 +5,6 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.Instance; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobId; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.yolean.Exceptions; @@ -47,7 +45,7 @@ public class DeploymentExpirer extends ControllerMaintainer { } } } - return asSuccessFactor(attempts, failures); + return asSuccessFactorDeviation(attempts, failures); } /** Returns whether given deployment has expired according to its TTL */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentInfoMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentInfoMaintainer.java index f6029eade37..d8d89177a9e 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentInfoMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentInfoMaintainer.java @@ -38,7 +38,7 @@ public class DeploymentInfoMaintainer extends ControllerMaintainer { } } } - return asSuccessFactor(attempts, failures); + return asSuccessFactorDeviation(attempts, failures); } private Collection<DeploymentId> instanceDeployments(Instance instance) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java index 6b058537c2d..c352fb053dc 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentIssueReporter.java @@ -77,7 +77,7 @@ public class DeploymentIssueReporter extends ControllerMaintainer { fileDeploymentIssueFor(application); else store(application.id(), null); - return 1.0; + return 0.0; } /** @@ -87,24 +87,24 @@ public class DeploymentIssueReporter extends ControllerMaintainer { */ private double maintainPlatformIssue(List<Application> applications) { if (controller().system() == SystemName.cd) - return 1.0; + return 0.0; VersionStatus versionStatus = controller().readVersionStatus(); Version systemVersion = controller().systemVersion(versionStatus); if (versionStatus.version(systemVersion).confidence() != broken) - return 1.0; + return 0.0; DeploymentStatusList statuses = controller().jobController().deploymentStatuses(ApplicationList.from(applications)); if (statuses.failingUpgradeToVersionSince(systemVersion, controller().clock().instant().minus(upgradeGracePeriod)).isEmpty()) - return 1.0; + return 0.0; List<ApplicationId> failingApplications = statuses.failingUpgradeToVersionSince(systemVersion, controller().clock().instant()) .mapToList(status -> status.application().id().defaultInstance()); // TODO jonmv: Send only tenant and application, here and elsewhere in this. deploymentIssues.fileUnlessOpen(failingApplications, systemVersion); - return 1.0; + return 0.0; } private Tenant ownerOf(TenantAndApplicationId applicationId) { @@ -145,7 +145,7 @@ public class DeploymentIssueReporter extends ControllerMaintainer { log.log(Level.INFO, "Exception caught when attempting to escalate issue with id '" + issueId + "': " + Exceptions.toMessageString(e)); } })); - return asSuccessFactor(attempts.get(), failures.get()); + return asSuccessFactorDeviation(attempts.get(), failures.get()); } private void store(TenantAndApplicationId id, IssueId issueId) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java index fa917d2eb4e..427ee7c4b06 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentMetricsMaintainer.java @@ -96,7 +96,7 @@ public class DeploymentMetricsMaintainer extends ControllerMaintainer { } catch (InterruptedException e) { throw new RuntimeException(e); } - return asSuccessFactor(attempts.get(), failures.get()); + return asSuccessFactorDeviation(attempts.get(), failures.get()); } static DeploymentMetrics updateDeploymentMetrics(DeploymentMetrics current, List<ClusterMetrics> metrics) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgrader.java index 934a1b4fa2f..8a3a2a11e09 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgrader.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DeploymentUpgrader.java @@ -78,7 +78,7 @@ public class DeploymentUpgrader extends ControllerMaintainer { ": " + Exceptions.toMessageString(e) + ". Retrying in " + interval()); } - return asSuccessFactor(attempts.get(), failures.get()); + return asSuccessFactorDeviation(attempts.get(), failures.get()); } /** Returns whether query and feed metrics are ~zero, or currently platform has been deployed for a week. */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainer.java index 7af96d10f2f..5218da91c46 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EnclaveAccessMaintainer.java @@ -26,7 +26,7 @@ public class EnclaveAccessMaintainer extends ControllerMaintainer { return controller().serviceRegistry().enclaveAccessService().allowAccessFor(externalAccounts()); } catch (RuntimeException e) { logger.log(WARNING, "Failed sharing resources with enclave", e); - return 0; + return 1.0; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java index 0b96d8adc1a..713782eb7b9 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainer.java @@ -75,10 +75,10 @@ public class EndpointCertificateMaintainer extends ControllerMaintainer { deleteOrReportUnmanagedCertificates(); } catch (Exception e) { log.log(Level.SEVERE, "Exception caught while maintaining endpoint certificates", e); - return 0.0; + return 1.0; } - return 1.0; + return 0.0; } private void updateRefreshedCertificates() { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/HostInfoUpdater.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/HostInfoUpdater.java index 1f21c688540..31236f4fcda 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/HostInfoUpdater.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/HostInfoUpdater.java @@ -73,7 +73,7 @@ public class HostInfoUpdater extends ControllerMaintainer { LOG.info("Updated information for " + hostsUpdated + " hosts(s)"); } } - return 1.0; + return 0.0; } private static Optional<String> modelNameOf(NodeEntity nodeEntity) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/InfrastructureUpgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/InfrastructureUpgrader.java index b051590ac5a..68c79fdca7e 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/InfrastructureUpgrader.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/InfrastructureUpgrader.java @@ -50,7 +50,7 @@ public abstract class InfrastructureUpgrader<TARGET extends VersionTarget> exten @Override protected double maintain() { return target().map(target -> upgradeAll(target, managedApplications)) - .orElse(1.0); + .orElse(0.0); } /** Deploy a list of system applications until they converge on the given version */ @@ -81,7 +81,7 @@ public abstract class InfrastructureUpgrader<TARGET extends VersionTarget> exten break; } } - return asSuccessFactor(attempts, failures); + return asSuccessFactorDeviation(attempts, failures); } /** Returns whether all applications have converged to the target version in zone */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java index 294d5bad42d..67188eb5e3a 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java @@ -90,7 +90,7 @@ public class MetricsReporter extends ControllerMaintainer { reportBrokenSystemVersion(versionStatus); reportTenantMetrics(); reportZmsQuotaMetrics(); - return 1.0; + return 0.0; } private void reportBrokenSystemVersion(VersionStatus versionStatus) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java index f930e64fc5a..f0d218ae6cf 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsUpgradeScheduler.java @@ -40,7 +40,7 @@ public class OsUpgradeScheduler extends ControllerMaintainer { if (!change.get().scheduleAt(now)) continue; controller().upgradeOsIn(cloud, change.get().version(), false); } - return 1.0; + return 0.0; } /** Returns the wanted change for cloud at given instant, if any */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsVersionStatusUpdater.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsVersionStatusUpdater.java index 119540eaa68..c643df6af68 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsVersionStatusUpdater.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OsVersionStatusUpdater.java @@ -22,12 +22,12 @@ public class OsVersionStatusUpdater extends ControllerMaintainer { try { OsVersionStatus newStatus = OsVersionStatus.compute(controller()); controller().updateOsVersionStatus(newStatus); - return 1.0; + return 0.0; } catch (Exception e) { log.log(Level.WARNING, "Failed to compute OS version status: " + Exceptions.toMessageString(e) + ". Retrying in " + interval()); } - return 0.0; + return 1.0; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ReindexingTriggerer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ReindexingTriggerer.java index 400673bfd0c..945b6d32a30 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ReindexingTriggerer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ReindexingTriggerer.java @@ -53,11 +53,11 @@ public class ReindexingTriggerer extends ControllerMaintainer { controller().applications().reindex(id, deployment.zone(), List.of(), List.of(), true, speed, "bakground reindexing, to account for changes in built-in linguistics components"); }); - return 1.0; + return 0.0; } catch (RuntimeException e) { log.log(Level.WARNING, "Failed to trigger reindexing: " + Exceptions.toMessageString(e)); - return 0.0; + return 1.0; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java index 3f20c2eac8f..52206d41c00 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java @@ -98,13 +98,13 @@ public class ResourceMeterMaintainer extends ControllerMaintainer { } catch (Exception e) { log.log(Level.WARNING, "Failed to collect resource snapshots. Retrying in " + interval() + ". Error: " + Exceptions.toMessageString(e)); - return 0.0; + return 1.0; } if (systemName.isPublic()) reportResourceSnapshots(resourceSnapshots); if (systemName.isPublic()) reportAllScalingEvents(); updateDeploymentCost(resourceSnapshots); - return 1.0; + return 0.0; } void updateDeploymentCost(Collection<ResourceSnapshot> resourceSnapshots) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceTagMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceTagMaintainer.java index 18ed154fcf1..59871f716e0 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceTagMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceTagMaintainer.java @@ -43,7 +43,7 @@ public class ResourceTagMaintainer extends ControllerMaintainer { if (taggedResources > 0) log.log(Level.INFO, "Tagged " + taggedResources + " resources in " + zone.getId()); }); - return 1.0; + return 0.0; } private Map<HostName, ApplicationId> getTenantOfParentHosts(ZoneId zoneId) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RetriggerMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RetriggerMaintainer.java index 74bb89e4105..aaf730cc158 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RetriggerMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RetriggerMaintainer.java @@ -47,9 +47,9 @@ public class RetriggerMaintainer extends ControllerMaintainer { controller().curator().writeRetriggerEntries(remaining); } catch (Exception e) { logger.log(Level.WARNING, "Exception while triggering jobs", e); - return 0.0; + return 1.0; } - return 1.0; + return 0.0; } /** Returns true if a job is ready to run, i.e. is currently not running */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleCleanupMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleCleanupMaintainer.java index f29df1bc0d5..e3a3415e170 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleCleanupMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleCleanupMaintainer.java @@ -26,6 +26,6 @@ public class TenantRoleCleanupMaintainer extends ControllerMaintainer { controller().serviceRegistry().tenantSecretService().cleanupSecretStores(deletedTenants); } - return 1.0; + return 0.0; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java index bd121871c7c..c7b236880fd 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java @@ -37,7 +37,7 @@ public class TenantRoleMaintainer extends ControllerMaintainer { controller().tenants().updateLastTenantRolesMaintained(t.name(), updated); }); - return 1.0; + return 0.0; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java index 82b3141e503..edcfcc317a7 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/Upgrader.java @@ -64,7 +64,7 @@ public class Upgrader extends ControllerMaintainer { for (UpgradePolicy policy : UpgradePolicy.values()) updateTargets(versionStatus, deploymentStatuses, policy); - return 1.0; + return 0.0; } private DeploymentStatusList deploymentStatuses(VersionStatus versionStatus) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/UserManagementMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/UserManagementMaintainer.java index 03987efab8b..7c4645a6e48 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/UserManagementMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/UserManagementMaintainer.java @@ -11,6 +11,7 @@ import java.util.Optional; import java.util.logging.Logger; import java.util.stream.Collectors; + /** * Maintains user management resources. * For now, ensures there's no discrepnacy between expected tenant/application roles and auth0/athenz roles @@ -46,7 +47,7 @@ public class UserManagementMaintainer extends ControllerMaintainer { }); } - return 1.0; + return 0.0; } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainer.java index da0fa890960..9271f870390 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VcmrMaintainer.java @@ -90,7 +90,7 @@ public class VcmrMaintainer extends ControllerMaintainer { } }); updateMetrics(); - return 1.0; + return 0.0; } /** diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdater.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdater.java index 154455c5198..1c4d13aa16d 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdater.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/VersionStatusUpdater.java @@ -39,12 +39,12 @@ public class VersionStatusUpdater extends ControllerMaintainer { controller().serviceRegistry().systemMonitor().reportSystemVersion(version.versionNumber(), convert(version.confidence())); }); - return 1.0; + return 0.0; } catch (Exception e) { log.log(Level.WARNING, "Failed to compute version status: " + Exceptions.toMessageString(e) + ". Retrying in " + interval()); } - return 0.0; + return 1.0; } static SystemMonitor.Confidence convert(VespaVersion.Confidence confidence) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java index e9947f3d565..45c00848407 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/versions/VespaVersion.java @@ -41,8 +41,8 @@ public record VespaVersion(Version version, .not().upgradingTo(statistics.version()); InstanceList failingOnThis = all.matching(instance -> statistics.failingUpgrades().stream().anyMatch(run -> run.id().application().equals(instance))); - // 'broken' if any canary fails - if ( ! failingOnThis.with(UpgradePolicy.canary).isEmpty()) + // 'broken' if any canary fails, and no non-canary is upgraded + if ( ! failingOnThis.with(UpgradePolicy.canary).isEmpty() && productionOnThis.not().with(UpgradePolicy.canary).isEmpty()) return Confidence.broken; // 'broken' if 6 non-canary was broken by this, and that is at least 5% of all diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java index a76d2eca521..ca31ceebc17 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java @@ -126,7 +126,7 @@ public class EndpointTest { Endpoint.of(instance1).target(cluster, prodZone).on(Port.tls()).in(SystemName.main), // Prod endpoint in CD - "https://cd.a1.t1.us-north-1.vespa.oath.cloud/", + "https://cd.a1.t1.us-north-1.cd.vespa.oath.cloud/", Endpoint.of(instance1).target(cluster, prodZone).on(Port.tls()).in(SystemName.cd), // Test endpoint in main @@ -300,7 +300,7 @@ public class EndpointTest { .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.main), - "cd.a2.t2.us-east-3-r.vespa.oath.cloud", + "cd.a2.t2.us-east-3-r.cd.vespa.oath.cloud", Endpoint.of(app2) .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) @@ -335,7 +335,7 @@ public class EndpointTest { .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.main), - "https://cd.a2.t2.a.vespa.oath.cloud/", + "https://cd.a2.t2.a.cd.vespa.oath.cloud/", Endpoint.of(app2) .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BcpGroupUpdaterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BcpGroupUpdaterTest.java index 5deba19c5ea..b8b05bfcfc1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BcpGroupUpdaterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BcpGroupUpdaterTest.java @@ -45,7 +45,7 @@ public class BcpGroupUpdaterTest { tester.controllerTester().upgradeSystem(Version.fromString("7.1")); var context = tester.newDeploymentContext(); var deploymentMetricsMaintainer = new DeploymentMetricsMaintainer(tester.controller(), Duration.ofDays(1)); - var updater = new BcpGroupUpdater(tester.controller(), Duration.ofDays(1)); + var updater = new BcpGroupUpdater(tester.controller(), Duration.ofDays(1), 1.0); ZoneId prod1 = ZoneId.from("prod", "ap-northeast-1"); ZoneId prod2 = ZoneId.from("prod", "us-east-3"); ZoneId prod3 = ZoneId.from("prod", "us-west-1"); @@ -57,7 +57,7 @@ public class BcpGroupUpdaterTest { setQpsMetric(50.0, context.application().id().defaultInstance(), prod1, tester); setBcpMetrics(1.5, 0.1, 0.45, context.instanceId(), prod1, "cluster1", tester); deploymentMetricsMaintainer.maintain(); - assertEquals(1.0, updater.maintain(), 0.0000001); + assertEquals(0.0, updater.maintain(), 0.0000001); assertTrafficFraction(1.0, 1.0, context.instanceId(), prod1, tester); assertNoBcpGroupInfo(context.instanceId(), prod1, "cluster1", tester, "No other regions in group"); @@ -67,7 +67,7 @@ public class BcpGroupUpdaterTest { setQpsMetric(20.0, context.application().id().defaultInstance(), prod2, tester); setBcpMetrics(100.0, 0.1, 0.45, context.instanceId(), prod1, "cluster1", tester); deploymentMetricsMaintainer.maintain(); - assertEquals(1.0, updater.maintain(), 0.0000001); + assertEquals(0.0, updater.maintain(), 0.0000001); assertTrafficFraction(0.75, 1.0, context.instanceId(), prod1, tester); assertTrafficFraction(0.25, 1.0, context.instanceId(), prod2, tester); assertNoBcpGroupInfo(context.instanceId(), prod1, "cluster1", tester, @@ -75,7 +75,7 @@ public class BcpGroupUpdaterTest { assertBcpGroupInfo(100.0, 0.1, 0.45, context.instanceId(), prod2, "cluster1", tester); setBcpMetrics(50.0, 0.2, 0.5, context.instanceId(), prod2, "cluster1", tester); - assertEquals(1.0, updater.maintain(), 0.0000001); + assertEquals(0.0, updater.maintain(), 0.0000001); assertBcpGroupInfo(50.0, 0.2, 0.5, context.instanceId(), prod1, "cluster1", tester); @@ -85,7 +85,7 @@ public class BcpGroupUpdaterTest { setQpsMetric(45.0, context.application().id().defaultInstance(), prod2, tester); setQpsMetric(02.0, context.application().id().defaultInstance(), prod3, tester); deploymentMetricsMaintainer.maintain(); - assertEquals(1.0, updater.maintain(), 0.0000001); + assertEquals(0.0, updater.maintain(), 0.0000001); assertTrafficFraction(0.53, 0.53 + (double)45/2 / 100, context.instanceId(), prod1, tester); assertTrafficFraction(0.45, 0.45 + (double)53/2 / 100, context.instanceId(), prod2, tester); assertTrafficFraction(0.02, 0.02 + (double)53/2 / 100, context.instanceId(), prod3, tester); @@ -129,7 +129,7 @@ public class BcpGroupUpdaterTest { locked -> tester.controller().applications().store(locked.with(deploymentSpec))); var deploymentMetricsMaintainer = new DeploymentMetricsMaintainer(tester.controller(), Duration.ofDays(1)); - var updater = new BcpGroupUpdater(tester.controller(), Duration.ofDays(1)); + var updater = new BcpGroupUpdater(tester.controller(), Duration.ofDays(1), 1.0); ZoneId ap1 = ZoneId.from("prod", "ap-northeast-1"); ZoneId ap2 = ZoneId.from("prod", "ap-southeast-1"); @@ -150,7 +150,7 @@ public class BcpGroupUpdaterTest { setQpsMetric(40.0, context.application().id().defaultInstance(), eu1, tester); deploymentMetricsMaintainer.maintain(); - assertEquals(1.0, updater.maintain(), 0.0000001); + assertEquals(0.0, updater.maintain(), 0.0000001); assertTrafficFraction(0.5, 0.5, context.instanceId(), ap1, tester); assertTrafficFraction(0.0, 0.5, context.instanceId(), ap2, tester); assertTrafficFraction(0.1, 0.1, context.instanceId(), us1, tester); @@ -197,7 +197,7 @@ public class BcpGroupUpdaterTest { locked -> tester.controller().applications().store(locked.with(deploymentSpec))); var deploymentMetricsMaintainer = new DeploymentMetricsMaintainer(tester.controller(), Duration.ofDays(1)); - var updater = new BcpGroupUpdater(tester.controller(), Duration.ofDays(1)); + var updater = new BcpGroupUpdater(tester.controller(), Duration.ofDays(1), 1.0); ZoneId ap1 = ZoneId.from("prod", "ap-northeast-1"); ZoneId ap2 = ZoneId.from("prod", "ap-southeast-1"); @@ -221,7 +221,7 @@ public class BcpGroupUpdaterTest { setQpsMetric(60.0, context.application().id().defaultInstance(), eu1, tester); deploymentMetricsMaintainer.maintain(); - assertEquals(1.0, updater.maintain(), 0.0000001); + assertEquals(0.0, updater.maintain(), 0.0000001); assertTrafficFraction(0.10, 0.10 + 50 / 200.0 / 1.5, context.instanceId(), ap1, tester); assertTrafficFraction(0.25, 0.25 + 30 / 200.0 / 1.5, context.instanceId(), ap2, tester); assertTrafficFraction(0.00, 0.00 + 40 / 200.0 / 2.5, context.instanceId(), us1, tester); @@ -242,7 +242,7 @@ public class BcpGroupUpdaterTest { setBcpMetrics(300, 0.3, 0.3, context.instanceId(), us3, "cluster2", tester); setBcpMetrics(100, 0.1, 0.1, context.instanceId(), eu1, "cluster2", tester); - assertEquals(1.0, updater.maintain(), 0.0000001); + assertEquals(0.0, updater.maintain(), 0.0000001); assertNoBcpGroupInfo(context.instanceId(), ap1, "cluster1", tester, "No info in ap"); assertNoBcpGroupInfo(context.instanceId(), ap2, "cluster1", tester, "No info in ap"); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainerTest.java index 63e2c99cb6e..6452edc9e61 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintainerTest.java @@ -38,14 +38,14 @@ public class ControllerMaintainerTest { void records_metric() { TestControllerMaintainer maintainer = new TestControllerMaintainer(tester.controller(), SystemName.main, new AtomicInteger()); maintainer.run(); - assertEquals(1.0, successFactorMetric(), 0.0000001); + assertEquals(0.0, successFactorDeviationMetric(), 0.0000001); maintainer.success = false; maintainer.run(); maintainer.run(); - assertEquals(0.0, successFactorMetric(), 0.0000001); + assertEquals(1.0, successFactorDeviationMetric(), 0.0000001); maintainer.success = true; maintainer.run(); - assertEquals(1.0, successFactorMetric(), 0.0000001); + assertEquals(0.0, successFactorDeviationMetric(), 0.0000001); } private long consecutiveFailuresMetric() { @@ -54,10 +54,10 @@ public class ControllerMaintainerTest { "maintenance.consecutiveFailures").get().longValue(); } - private long successFactorMetric() { + private long successFactorDeviationMetric() { MetricsMock metrics = (MetricsMock) tester.controller().metric(); return metrics.getMetric((context) -> "TestControllerMaintainer".equals(context.get("job")), - "maintenance.successFactor").get().longValue(); + "maintenance.successFactorDeviation").get().longValue(); } private static class TestControllerMaintainer extends ControllerMaintainer { @@ -73,7 +73,7 @@ public class ControllerMaintainerTest { @Override protected double maintain() { executions.incrementAndGet(); - return success ? 1.0 : 0.0; + return success ? 0.0 : 1.0; } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java index 934e15ad623..49cf8c634ba 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/EndpointCertificateMaintainerTest.java @@ -49,7 +49,7 @@ public class EndpointCertificateMaintainerTest { @Test void old_and_unused_cert_is_deleted() { tester.curator().writeEndpointCertificateMetadata(ApplicationId.defaultId(), exampleMetadata); - assertEquals(1.0, maintainer.maintain(), 0.0000001); + assertEquals(0.0, maintainer.maintain(), 0.0000001); assertTrue(tester.curator().readEndpointCertificateMetadata(ApplicationId.defaultId()).isEmpty()); } @@ -57,7 +57,7 @@ public class EndpointCertificateMaintainerTest { void unused_but_recently_used_cert_is_not_deleted() { EndpointCertificateMetadata recentlyRequestedCert = exampleMetadata.withLastRequested(tester.clock().instant().minusSeconds(3600).getEpochSecond()); tester.curator().writeEndpointCertificateMetadata(ApplicationId.defaultId(), recentlyRequestedCert); - assertEquals(1.0, maintainer.maintain(), 0.0000001); + assertEquals(0.0, maintainer.maintain(), 0.0000001); assertEquals(Optional.of(recentlyRequestedCert), tester.curator().readEndpointCertificateMetadata(ApplicationId.defaultId())); } @@ -69,7 +69,7 @@ public class EndpointCertificateMaintainerTest { secretStore.setSecret(exampleMetadata.keyName(), "foo", 1); secretStore.setSecret(exampleMetadata.certName(), "bar", 1); - assertEquals(1.0, maintainer.maintain(), 0.0000001); + assertEquals(0.0, maintainer.maintain(), 0.0000001); var updatedCert = Optional.of(recentlyRequestedCert.withLastRefreshed(tester.clock().instant().getEpochSecond()).withVersion(1)); @@ -90,7 +90,7 @@ public class EndpointCertificateMaintainerTest { deploymentContext.submit(applicationPackage).runJob(systemTest).runJob(stagingTest).runJob(productionUsWest1); - assertEquals(1.0, maintainer.maintain(), 0.0000001); + assertEquals(0.0, maintainer.maintain(), 0.0000001); var metadata = tester.curator().readEndpointCertificateMetadata(appId).orElseThrow(); tester.controller().serviceRegistry().endpointCertificateProvider().certificateDetails(metadata.rootRequestId()); // cert should not be deleted, the app is deployed! } @@ -110,7 +110,7 @@ public class EndpointCertificateMaintainerTest { var originalMetadata = tester.curator().readEndpointCertificateMetadata(appId).orElseThrow(); // cert should not be deleted, the app is deployed! - assertEquals(1.0, maintainer.maintain(), 0.0000001); + assertEquals(0.0, maintainer.maintain(), 0.0000001); assertEquals(tester.curator().readEndpointCertificateMetadata(appId), Optional.of(originalMetadata)); tester.controller().serviceRegistry().endpointCertificateProvider().certificateDetails(originalMetadata.rootRequestId()); @@ -121,7 +121,7 @@ public class EndpointCertificateMaintainerTest { tester.controller().serviceRegistry().endpointCertificateProvider().requestCaSignedCertificate(appId, originalMetadata.requestedDnsSans(), Optional.of(originalMetadata)); // We should now pick up the new key and cert version + uuid, but not force trigger deployment yet - assertEquals(1.0, maintainer.maintain(), 0.0000001); + assertEquals(0.0, maintainer.maintain(), 0.0000001); deploymentContext.assertNotRunning(productionUsWest1); var updatedMetadata = tester.curator().readEndpointCertificateMetadata(appId).orElseThrow(); assertNotEquals(originalMetadata.leafRequestId().orElseThrow(), updatedMetadata.leafRequestId().orElseThrow()); @@ -130,7 +130,7 @@ public class EndpointCertificateMaintainerTest { // after another 4 days, we should force trigger deployment if it hasn't already happened tester.clock().advance(Duration.ofDays(4).plusSeconds(1)); deploymentContext.assertNotRunning(productionUsWest1); - assertEquals(1.0, maintainer.maintain(), 0.0000001); + assertEquals(0.0, maintainer.maintain(), 0.0000001); deploymentContext.assertRunning(productionUsWest1); } @@ -156,7 +156,7 @@ public class EndpointCertificateMaintainerTest { ApplicationId unknown = ApplicationId.fromSerializedForm("applicationid:is:unknown"); endpointCertificateProvider.requestCaSignedCertificate(unknown, List.of("a", "b", "c"), Optional.empty()); // Unknown to controller! - assertEquals(1.0, maintainer.maintain(), 0.0000001); + assertEquals(0.0, maintainer.maintain(), 0.0000001); assertTrue(endpointCertificateProvider.dnsNamesOf(unknown).isEmpty()); assertTrue(endpointCertificateProvider.listCertificates().isEmpty()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java index 43ad01fc5c2..168a1345c39 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java @@ -18,7 +18,6 @@ import org.junit.jupiter.api.Test; import java.net.URI; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -155,7 +154,7 @@ public class RotationRepositoryTest { var application2 = tester.newDeploymentContext("tenant2", "app2", "default"); application2.submit(applicationPackage).deploy(); assertEquals(List.of(new RotationId("foo-1")), rotationIds(application2.instance().rotations())); - assertEquals("https://cd.app2.tenant2.global.vespa.oath.cloud/", + assertEquals("https://cd.app2.tenant2.global.cd.vespa.oath.cloud/", tester.controller().routing().readDeclaredEndpointsOf(application2.instanceId()).primary().get().url().toString()); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java index f08e92a515d..7afa5c7f44a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/versions/VersionStatusTest.java @@ -349,6 +349,13 @@ public class VersionStatusTest { assertEquals(Confidence.high, confidence(tester.controller(), version0), "Confidence remains unchanged for version0: High"); assertEquals(VespaVersion.Confidence.high, confidence(tester.controller(), version2), "90% of defaults deployed successfully: High"); + // Canary failing a new revision does not affect confidence + canary0.submit(canaryPolicy).failDeployment(systemTest); + tester.controllerTester().computeVersionStatus(); + assertEquals(Confidence.high, confidence(tester.controller(), version0), "Confidence remains unchanged for version0: High"); + assertEquals(VespaVersion.Confidence.high, confidence(tester.controller(), version2), "90% of defaults deployed successfully: High"); + canary0.deploy(); + // A new version is released, all canaries upgrade successfully, but enough "default" apps fail to mark version // as broken Version version3 = new Version("6.5"); diff --git a/default_build_settings.cmake b/default_build_settings.cmake index c82bb91accc..2785a98a396 100644 --- a/default_build_settings.cmake +++ b/default_build_settings.cmake @@ -4,11 +4,11 @@ include(VespaExtendedDefaultBuildSettings OPTIONAL) function(setup_vespa_default_build_settings_darwin) message("-- Setting up default build settings for darwin") - set(DEFAULT_EXTRA_LINK_DIRECTORY "${VESPA_DEPS_PREFIX}/lib" "/usr/local/opt/bison/lib" "/usr/local/opt/flex/lib" "/usr/local/opt/icu4c/lib" "/usr/local/opt/openssl@1.1/lib" "/usr/local/opt/openblas/lib") - list(APPEND DEFAULT_EXTRA_LINK_DIRECTORY "/usr/local/lib") + set(DEFAULT_EXTRA_LINK_DIRECTORY "${VESPA_DEPS_PREFIX}/lib" "${VESPA_HOMEBREW_PREFIX}/opt/bison/lib" "${VESPA_HOMEBREW_PREFIX}/opt/flex/lib" "${VESPA_HOMEBREW_PREFIX}/opt/icu4c/lib" "${VESPA_HOMEBREW_PREFIX}/opt/openssl@1.1/lib" "${VESPA_HOMEBREW_PREFIX}/opt/openblas/lib") + list(APPEND DEFAULT_EXTRA_LINK_DIRECTORY "${VESPA_HOMEBREW_PREFIX}/lib") set(DEFAULT_EXTRA_LINK_DIRECTORY "${DEFAULT_EXTRA_LINK_DIRECTORY}" PARENT_SCOPE) - set(DEFAULT_EXTRA_INCLUDE_DIRECTORY "${VESPA_DEPS_PREFIX}/include" "/usr/local/opt/flex/include" "/usr/local/opt/icu4c/include" "/usr/local/opt/openssl@1.1/include" "/usr/local/opt/openblas/include") - list(APPEND DEFAULT_EXTRA_INCLUDE_DIRECTORY "/usr/local/include") + set(DEFAULT_EXTRA_INCLUDE_DIRECTORY "${VESPA_DEPS_PREFIX}/include" "${VESPA_HOMEBREW_PREFIX}/opt/flex/include" "${VESPA_HOMEBREW_PREFIX}/opt/icu4c/include" "${VESPA_HOMEBREW_PREFIX}/opt/openssl@1.1/include" "${VESPA_HOMEBREW_PREFIX}/opt/openblas/include") + list(APPEND DEFAULT_EXTRA_INCLUDE_DIRECTORY "${VESPA_HOMEBREW_PREFIX}/include") set(DEFAULT_EXTRA_INCLUDE_DIRECTORY "${DEFAULT_EXTRA_INCLUDE_DIRECTORY}" PARENT_SCOPE) endfunction() @@ -84,7 +84,7 @@ endfunction() function(vespa_use_default_cmake_prefix_path) set(DEFAULT_CMAKE_PREFIX_PATH ${VESPA_DEPS_PREFIX}) if (APPLE) - list(APPEND DEFAULT_CMAKE_PREFIX_PATH "/usr/local/opt/bison" "/usr/local/opt/flex" "/usr/local/opt/openssl@1.1" "/usr/local/opt/openblas" "/usr/local/opt/icu4c") + list(APPEND DEFAULT_CMAKE_PREFIX_PATH "${VESPA_HOMEBREW_PREFIX}/opt/bison" "${VESPA_HOMEBREW_PREFIX}/opt/flex" "${VESPA_HOMEBREW_PREFIX}/opt/openssl@1.1" "${VESPA_HOMEBREW_PREFIX}/opt/openblas" "${VESPA_HOMEBREW_PREFIX}/opt/icu4c") endif() message("-- DEFAULT_CMAKE_PREFIX_PATH is ${DEFAULT_CMAKE_PREFIX_PATH}") if(NOT DEFINED CMAKE_PREFIX_PATH) @@ -203,8 +203,8 @@ function(vespa_use_default_cxx_compiler) unset(DEFAULT_CMAKE_CXX_COMPILER) if(NOT DEFINED VESPA_COMPILER_VARIANT OR VESPA_COMPILER_VARIANT STREQUAL "gcc") if(APPLE) - set(DEFAULT_CMAKE_C_COMPILER "/usr/local/bin/gcc-12") - set(DEFAULT_CMAKE_CXX_COMPILER "/usr/local/bin/g++-12") + set(DEFAULT_CMAKE_C_COMPILER "${VESPA_HOMEBREW_PREFIX}/bin/gcc-12") + set(DEFAULT_CMAKE_CXX_COMPILER "${VESPA_HOMEBREW_PREFIX}/bin/g++-12") elseif(VESPA_OS_DISTRO_COMBINED STREQUAL "amzn 2") set(DEFAULT_CMAKE_C_COMPILER "/usr/bin/gcc10-gcc") set(DEFAULT_CMAKE_CXX_COMPILER "/usr/bin/gcc10-g++") @@ -215,8 +215,8 @@ function(vespa_use_default_cxx_compiler) endif() elseif(VESPA_COMPILER_VARIANT STREQUAL "clang") if(APPLE) - set(DEFAULT_CMAKE_C_COMPILER, "/usr/local/opt/llvm/bin/clang") - set(DEFAULT_CMAKE_CXX_COMPILER "/usr/local/opt/llvm/bin/clang++") + set(DEFAULT_CMAKE_C_COMPILER, "${VESPA_HOMEBREW_PREFIX}/opt/llvm/bin/clang") + set(DEFAULT_CMAKE_CXX_COMPILER "${VESPA_HOMEBREW_PREFIX}/opt/llvm/bin/clang++") elseif(EXISTS "/usr/bin/clang" AND EXISTS "/usr/bin/clang++") set(DEFAULT_CMAKE_C_COMPILER "/usr/bin/clang") set(DEFAULT_CMAKE_CXX_COMPILER "/usr/bin/clang++") diff --git a/document/src/vespa/document/select/parse_utils.cpp b/document/src/vespa/document/select/parse_utils.cpp index 95461442349..4c116d5bff4 100644 --- a/document/src/vespa/document/select/parse_utils.cpp +++ b/document/src/vespa/document/select/parse_utils.cpp @@ -24,7 +24,7 @@ parse_i64(const char* str, size_t len, int64_t& out) { } bool parse_double(const char* str, size_t len, double& out) { -#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000 +#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 170000 // Temporary workaround that also handles underflow (cf. issue 3081) // until libc++ supports std::from_chars for double char *str_end = const_cast<char*>(str) + len; diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index 299d8c5d1e3..f24e9b9b7a5 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -57,7 +57,7 @@ public class Flags { public static final UnboundBooleanFlag DROP_CACHES = defineFeatureFlag( "drop-caches", false, - List.of("hakonhall", "baldersheim"), "2023-03-06", "2023-04-05", + List.of("hakonhall", "baldersheim"), "2023-03-06", "2023-06-05", "Drop caches on tenant hosts", "Takes effect on next tick", ZONE_ID, diff --git a/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java index 336edf74e8f..dea0dbd3623 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java @@ -347,6 +347,12 @@ public class PermanentFlags { "Takes effect immediately", TENANT_ID); + public static final UnboundIntFlag KEEP_FILE_REFERENCES_ON_TENANT_NODES = defineIntFlag( + "keep-file-references-on-tenant-nodes", 14, + "How many days to keep file references on tenant nodes (based on last modification time)", + "Takes effect on restart of Docker container", + ZONE_ID, APPLICATION_ID + ); private PermanentFlags() {} diff --git a/functions.cmake b/functions.cmake index 7fa0b0db954..7f217867314 100644 --- a/functions.cmake +++ b/functions.cmake @@ -746,6 +746,14 @@ function(vespa_detect_build_platform) elseif(APPLE) set(OS_DISTRO "darwin") set(OS_DISTRO_VERSION ${CMAKE_SYSTEM_VERSION}) + if(EXISTS "/opt/homebrew/bin/brew") + set(VESPA_HOMEBREW_PREFIX "/opt/homebrew") + elseif(EXISTS "/usr/local/bin/brew") + set(VESPA_HOMEBREW_PREFIX "/usr/local") + else() + message(FATAL_ERROR "-- Cannot determine homebrew prefix") + endif() + set(VESPA_HOMEBREW_PREFIX ${VESPA_HOMEBREW_PREFIX} PARENT_SCOPE) endif() if(OS_DISTRO) set(VESPA_OS_DISTRO ${OS_DISTRO} PARENT_SCOPE) diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoreDumpMetadata.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoreDumpMetadata.java index 7367a254b4a..eceb83a6cba 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoreDumpMetadata.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/CoreDumpMetadata.java @@ -12,6 +12,9 @@ import java.util.Optional; * @author hakonhall */ public class CoreDumpMetadata { + public enum Type { CORE_DUMP, JVM_HEAP, OOM } + + private Type type; private String binPath; private List<String> backtrace; private List<String> backtraceAllThreads; @@ -24,30 +27,33 @@ public class CoreDumpMetadata { public CoreDumpMetadata() {} - public Optional<String> binPath() { return Optional.ofNullable(binPath); }; - public Optional<List<String>> backtrace() { return Optional.ofNullable(backtrace); }; - public Optional<List<String>> backtraceAllThreads() { return Optional.ofNullable(backtraceAllThreads); }; - public Optional<Path> coredumpPath() { return Optional.ofNullable(coreDumpPath); }; + public Optional<Type> type() { return Optional.ofNullable(type); } + public Optional<String> binPath() { return Optional.ofNullable(binPath); } + public Optional<List<String>> backtrace() { return Optional.ofNullable(backtrace); } + public Optional<List<String>> backtraceAllThreads() { return Optional.ofNullable(backtraceAllThreads); } + public Optional<Path> coredumpPath() { return Optional.ofNullable(coreDumpPath); } public Optional<String> decryptionToken() { return Optional.ofNullable(decryptionToken); } - public Optional<String> kernelVersion() { return Optional.ofNullable(kernelVersion); }; - public Optional<String> cpuMicrocodeVersion() { return Optional.ofNullable(cpuMicrocodeVersion); }; - public Optional<DockerImage> dockerImage() { return Optional.ofNullable(dockerImage); }; - public Optional<String> vespaVersion() { return Optional.ofNullable(vespaVersion); }; + public Optional<String> kernelVersion() { return Optional.ofNullable(kernelVersion); } + public Optional<String> cpuMicrocodeVersion() { return Optional.ofNullable(cpuMicrocodeVersion); } + public Optional<DockerImage> dockerImage() { return Optional.ofNullable(dockerImage); } + public Optional<String> vespaVersion() { return Optional.ofNullable(vespaVersion); } - public CoreDumpMetadata setBinPath(String binPath) { this.binPath = binPath; return this; }; - public CoreDumpMetadata setBacktrace(List<String> backtrace) { this.backtrace = backtrace; return this; }; - public CoreDumpMetadata setBacktraceAllThreads(List<String> backtraceAllThreads) { this.backtraceAllThreads = backtraceAllThreads; return this; }; - public CoreDumpMetadata setCoreDumpPath(Path coreDumpPath) { this.coreDumpPath = coreDumpPath; return this; }; + public CoreDumpMetadata setType(Type type) { this.type = type; return this; } + public CoreDumpMetadata setBinPath(String binPath) { this.binPath = binPath; return this; } + public CoreDumpMetadata setBacktrace(List<String> backtrace) { this.backtrace = backtrace; return this; } + public CoreDumpMetadata setBacktraceAllThreads(List<String> backtraceAllThreads) { this.backtraceAllThreads = backtraceAllThreads; return this; } + public CoreDumpMetadata setCoreDumpPath(Path coreDumpPath) { this.coreDumpPath = coreDumpPath; return this; } public CoreDumpMetadata setDecryptionToken(String decryptionToken) { this.decryptionToken = decryptionToken; return this; } - public CoreDumpMetadata setKernelVersion(String kernelVersion) { this.kernelVersion = kernelVersion; return this; }; - public CoreDumpMetadata setCpuMicrocodeVersion(String cpuMicrocodeVersion) { this.cpuMicrocodeVersion = cpuMicrocodeVersion; return this; }; - public CoreDumpMetadata setDockerImage(DockerImage dockerImage) { this.dockerImage = dockerImage; return this; }; - public CoreDumpMetadata setVespaVersion(String vespaVersion) { this.vespaVersion = vespaVersion; return this; }; + public CoreDumpMetadata setKernelVersion(String kernelVersion) { this.kernelVersion = kernelVersion; return this; } + public CoreDumpMetadata setCpuMicrocodeVersion(String cpuMicrocodeVersion) { this.cpuMicrocodeVersion = cpuMicrocodeVersion; return this; } + public CoreDumpMetadata setDockerImage(DockerImage dockerImage) { this.dockerImage = dockerImage; return this; } + public CoreDumpMetadata setVespaVersion(String vespaVersion) { this.vespaVersion = vespaVersion; return this; } @Override public String toString() { return "CoreDumpMetadata{" + - "binPath=" + binPath + + "type=" + type + + ", binPath=" + binPath + ", backtrace=" + backtrace + ", backtraceAllThreads=" + backtraceAllThreads + ", coreDumpPath=" + coreDumpPath + @@ -64,7 +70,8 @@ public class CoreDumpMetadata { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CoreDumpMetadata metadata = (CoreDumpMetadata) o; - return Objects.equals(binPath, metadata.binPath) && + return type == metadata.type && + Objects.equals(binPath, metadata.binPath) && Objects.equals(backtrace, metadata.backtrace) && Objects.equals(backtraceAllThreads, metadata.backtraceAllThreads) && Objects.equals(coreDumpPath, metadata.coreDumpPath) && @@ -77,7 +84,7 @@ public class CoreDumpMetadata { @Override public int hashCode() { - return Objects.hash(binPath, backtrace, backtraceAllThreads, coreDumpPath, decryptionToken, kernelVersion, + return Objects.hash(type, binPath, backtrace, backtraceAllThreads, coreDumpPath, decryptionToken, kernelVersion, cpuMicrocodeVersion, dockerImage, vespaVersion); } } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/bindings/ReportCoreDumpRequest.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/bindings/ReportCoreDumpRequest.java index 27cf28b8e1e..cf717aff787 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/bindings/ReportCoreDumpRequest.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/cores/bindings/ReportCoreDumpRequest.java @@ -31,6 +31,7 @@ public class ReportCoreDumpRequest { public List<String> backtrace; public List<String> backtrace_all_threads; + public String type; public String bin_path; public String coredump_path; public String cpu_microcode_version; @@ -44,6 +45,7 @@ public class ReportCoreDumpRequest { /** Fill this from metadata and return this. */ @JsonIgnore public ReportCoreDumpRequest fillFrom(CoreDumpMetadata metadata) { + metadata.type().ifPresent(type -> this.type = type.name()); metadata.binPath().ifPresent(binPath -> this.bin_path = binPath); metadata.backtrace().ifPresent(backtrace -> this.backtrace = List.copyOf(backtrace)); metadata.backtraceAllThreads().ifPresent(backtraceAllThreads -> this.backtrace_all_threads = List.copyOf(backtraceAllThreads)); diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java index 28773767d24..4761b6da421 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollector.java @@ -99,14 +99,15 @@ public class CoreCollector { var metadata = new CoreDumpMetadata(); if (JAVA_HEAP_DUMP_PATTERN.matcher(coredumpPath.getFileName().toString()).find()) { - metadata.setBinPath("java") + metadata.setType(CoreDumpMetadata.Type.JVM_HEAP) + .setBinPath("java") .setBacktrace(List.of("Heap dump, no backtrace available")); return metadata; } try { String binPath = readBinPath(context, coredumpPath); - metadata.setBinPath(binPath); + metadata.setType(CoreDumpMetadata.Type.CORE_DUMP).setBinPath(binPath); if (Path.of(binPath).getFileName().toString().equals("java")) { metadata.setBacktraceAllThreads(readJstack(context, coredumpPath, binPath)); diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/systemd/SystemCtl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/systemd/SystemCtl.java index c7d34a12f43..9662d4184df 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/systemd/SystemCtl.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/systemd/SystemCtl.java @@ -91,7 +91,7 @@ public class SystemCtl { public String getServiceProperty(TaskContext context, String unit, String property) { return newCommandLine(context) .add("systemctl", "show", "--property", property, "--value", unit + ".service") - .execute() + .executeSilently() .getOutput(); } diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java index 4fa18c71da0..2ca9e084d96 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoreCollectorTest.java @@ -143,6 +143,7 @@ public class CoreCollectorTest { String.join("\n", GDB_BACKTRACE)); var expected = new CoreDumpMetadata().setBinPath(TEST_BIN_PATH) + .setType(CoreDumpMetadata.Type.CORE_DUMP) .setBacktrace(GDB_BACKTRACE) .setBacktraceAllThreads(GDB_BACKTRACE); assertEquals(expected, coreCollector.collect(context, TEST_CORE_PATH)); @@ -156,7 +157,7 @@ public class CoreCollectorTest { mockExec(new String[]{GDB_PATH_RHEL8 + " -n -ex set print frame-arguments none -ex bt -batch /usr/bin/program /tmp/core.1234"}, "", "Failure"); - var expected = new CoreDumpMetadata().setBinPath(TEST_BIN_PATH); + var expected = new CoreDumpMetadata().setBinPath(TEST_BIN_PATH).setType(CoreDumpMetadata.Type.CORE_DUMP); assertEquals(expected, coreCollector.collect(context, TEST_CORE_PATH)); } @@ -174,6 +175,7 @@ public class CoreCollectorTest { jstack); var expected = new CoreDumpMetadata().setBinPath(jdkPath) + .setType(CoreDumpMetadata.Type.CORE_DUMP) .setBacktraceAllThreads(List.of(jstack)); assertEquals(expected, coreCollector.collect(context, TEST_CORE_PATH)); } @@ -181,6 +183,7 @@ public class CoreCollectorTest { @Test void metadata_for_java_heap_dump() { var expected = new CoreDumpMetadata().setBinPath("java") + .setType(CoreDumpMetadata.Type.JVM_HEAP) .setBacktrace(List.of("Heap dump, no backtrace available")); assertEquals(expected, coreCollector.collect(context, context.paths().of("/dump_java_pid123.hprof"))); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java index 4c9fab748d1..856d6e07156 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java @@ -59,7 +59,7 @@ public class AutoscalingMaintainer extends NodeRepositoryMaintainer { failures++; } } - return asSuccessFactor(attempts, failures); + return asSuccessFactorDeviation(attempts, failures); } /** diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DiskReplacer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DiskReplacer.java index acd5cb61d81..6f2eb726e91 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DiskReplacer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DiskReplacer.java @@ -51,6 +51,6 @@ public class DiskReplacer extends NodeRepositoryMaintainer { log.log(Level.WARNING, "Failed to rebuild " + host.hostname() + ", will retry in " + interval(), e); } } - return this.asSuccessFactor(nodes.size(), failures); + return this.asSuccessFactorDeviation(nodes.size(), failures); } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java index 83dadddf76c..a4bc3a1aea5 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java @@ -113,7 +113,7 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer { } success++; } - return asSuccessFactor(attempts, attempts - success); + return asSuccessFactorDeviation(attempts, attempts - success); } /** diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostDeprovisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostDeprovisioner.java index 7ecfc8f7926..0fa16f22061 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostDeprovisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostDeprovisioner.java @@ -53,7 +53,7 @@ public class HostDeprovisioner extends NodeRepositoryMaintainer { log.log(Level.WARNING, "Failed to deprovision " + host.hostname() + ", will retry in " + interval(), e); } } - return asSuccessFactor(hosts.size(), failures); + return asSuccessFactorDeviation(hosts.size(), failures); } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisioner.java index 86c5a926900..3c77725298d 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisioner.java @@ -63,7 +63,7 @@ public class HostResumeProvisioner extends NodeRepositoryMaintainer { } } } - return asSuccessFactor(hosts.size(), failures); + return asSuccessFactorDeviation(hosts.size(), failures); } private void setIpConfig(Node host, NodeList children, HostIpConfig hostIpConfig) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java index f864ab18920..baa2e596b36 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java @@ -94,7 +94,7 @@ public class LoadBalancerExpirer extends NodeRepositoryMaintainer { .collect(Collectors.joining(", ")), interval())); } - return asSuccessFactor(attempts.get(), failed.size()); + return asSuccessFactorDeviation(attempts.get(), failed.size()); } /** Remove reals from inactive load balancers */ @@ -131,7 +131,7 @@ public class LoadBalancerExpirer extends NodeRepositoryMaintainer { interval()), lastException.get()); } - return asSuccessFactor(attempts.get(), failed.size()); + return asSuccessFactorDeviation(attempts.get(), failed.size()); } /** Patch load balancers matching given filter, while holding lock */ diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailer.java index afea08711fa..f9ff2f08375 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailer.java @@ -97,7 +97,7 @@ public class NodeFailer extends NodeRepositoryMaintainer { metric.set(throttlingActiveMetric, throttlingActive, null); metric.set(throttledHostFailuresMetric, throttledHostFailures, null); metric.set(throttledNodeFailuresMetric, throttledNodeFailures, null); - return asSuccessFactor(attempts, failures); + return asSuccessFactorDeviation(attempts, failures); } private Collection<FailingNode> findActiveFailingNodes() { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeHealthTracker.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeHealthTracker.java index 781debe26a0..979a2771082 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeHealthTracker.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeHealthTracker.java @@ -77,7 +77,7 @@ public class NodeHealthTracker extends NodeRepositoryMaintainer { failures.add(1); } }); - return asSuccessFactor(attempts.get(), failures.get()); + return asSuccessFactorDeviation(attempts.get(), failures.get()); } /** diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java index b299369db1a..e28dac1c915 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainer.java @@ -55,10 +55,10 @@ public class NodeMetricsDbMaintainer extends NodeRepositoryMaintainer { nodeRepository().metricsDb().gc(); - return asSuccessFactor(attempts, failures.get()); + return asSuccessFactorDeviation(attempts, failures.get()); } catch (InterruptedException e) { - return asSuccessFactor(attempts, failures.get()); + return asSuccessFactorDeviation(attempts, failures.get()); } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintainer.java index 3c00e3b708d..b8d37c7eb5c 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintainer.java @@ -59,7 +59,7 @@ public abstract class NodeRepositoryMaintainer extends Maintainer { @Override public void completed(String job, double successFactor, long duration) { var context = metric.createContext(Map.of("job", job)); - metric.set("maintenance.successFactor", successFactor, context); + metric.set("maintenance.successFactorDeviation", successFactor, context); metric.set("maintenance.duration", duration, context); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainer.java index af368934188..4071559d841 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainer.java @@ -48,7 +48,7 @@ public class ScalingSuggestionsMaintainer extends NodeRepositoryMaintainer { failures++; } } - return asSuccessFactor(attempts, failures); + return asSuccessFactorDeviation(attempts, failures); } private Applications applications() { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java index 04f64b070b3..890d190c24e 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java @@ -109,7 +109,7 @@ public class LoadBalancerProvisioner { public void activate(Set<ClusterSpec> clusters, NodeList newActive, ApplicationTransaction transaction) { Map<ClusterSpec.Id, ZoneEndpoint> activatingClusters = clusters.stream() // .collect(Collectors.toMap(ClusterSpec::id, ClusterSpec::zoneEndpoint)); - // TODO: this dies with combined clusters Ü + // TODO: this dies with combined clusters .collect(groupingBy(LoadBalancerProvisioner::effectiveId, reducing(ZoneEndpoint.defaultEndpoint, ClusterSpec::zoneEndpoint, @@ -193,14 +193,13 @@ public class LoadBalancerProvisioner { Optional<LoadBalancer> loadBalancer = db.readLoadBalancer(id); LoadBalancer newLoadBalancer; LoadBalancer.State fromState = loadBalancer.map(LoadBalancer::state).orElse(null); - if ( loadBalancer.isPresent() - && ( ! inAccount(cloudAccount, loadBalancer.get()) - || ! hasCorrectVisibility(loadBalancer.get(), zoneEndpoint))) { - // We have a load balancer, but with the wrong account or visibility. - // Load balancer must be removed before we can provision a new one with the wanted visibility - newLoadBalancer = loadBalancer.get().with(LoadBalancer.State.removable, now); - } - else { + boolean recreateLoadBalancer = loadBalancer.isPresent() && (!inAccount(cloudAccount, loadBalancer.get()) + || !hasCorrectVisibility(loadBalancer.get(), zoneEndpoint)); + if (recreateLoadBalancer) { + // We have a load balancer, but with the wrong account or visibility. + // Load balancer must be removed before we can provision a new one with the wanted visibility + newLoadBalancer = loadBalancer.get().with(LoadBalancer.State.removable, now); + } else { Optional<LoadBalancerInstance> instance = provisionInstance(id, loadBalancer, zoneEndpoint, cloudAccount); newLoadBalancer = loadBalancer.isEmpty() ? new LoadBalancer(id, instance, LoadBalancer.State.reserved, now) : loadBalancer.get().with(instance); @@ -211,8 +210,8 @@ public class LoadBalancerProvisioner { } private static boolean hasCorrectVisibility(LoadBalancer newLoadBalancer, ZoneEndpoint zoneEndpoint) { - return newLoadBalancer.instance().isEmpty() - || newLoadBalancer.instance().get().settings().isPublicEndpoint() == zoneEndpoint.isPublicEndpoint(); + return newLoadBalancer.instance().isEmpty() || + newLoadBalancer.instance().get().settings().isPublicEndpoint() == zoneEndpoint.isPublicEndpoint(); } private void activate(ApplicationTransaction transaction, ClusterSpec.Id cluster, ZoneEndpoint settings, NodeList nodes) { @@ -320,14 +319,6 @@ public class LoadBalancerProvisioner { return loadBalancer.instance().isEmpty() || loadBalancer.instance().get().cloudAccount().equals(cloudAccount); } - /** Returns whether load balancer has given reals, and settings if specified */ - private static boolean isUpToDate(LoadBalancer loadBalancer, Set<Real> reals, ZoneEndpoint zoneEndpoint) { - if (loadBalancer.instance().isEmpty()) - throw new IllegalStateException("Expected a load balancer instance to be present, for " + loadBalancer.id()); - return loadBalancer.instance().get().reals().equals(reals) - && loadBalancer.instance().get().settings().equals(zoneEndpoint); - } - /** Find IP addresses reachable by the load balancer service */ private Set<String> reachableIpAddresses(Node node) { Set<String> reachable = new LinkedHashSet<>(node.ipConfig().primary()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainerTest.java index c7c6e770fe3..611594cc72e 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeMetricsDbMaintainerTest.java @@ -44,7 +44,7 @@ public class NodeMetricsDbMaintainerTest { fetcher, Duration.ofHours(1), new TestMetric()); - assertEquals(maintainer.maintain(), 1.0, 0.0000001); + assertEquals(maintainer.maintain(), 0.0, 0.0000001); List<NodeTimeseries> timeseriesList = tester.nodeRepository().metricsDb().getNodeTimeseries(Duration.ofDays(1), Set.of("host-1.yahoo.com", "host-2.yahoo.com")); assertEquals(2, timeseriesList.size()); diff --git a/searchcore/src/tests/proton/common/attribute_updater/attribute_updater_test.cpp b/searchcore/src/tests/proton/common/attribute_updater/attribute_updater_test.cpp index 392dcc256c8..892be2c874f 100644 --- a/searchcore/src/tests/proton/common/attribute_updater/attribute_updater_test.cpp +++ b/searchcore/src/tests/proton/common/attribute_updater/attribute_updater_test.cpp @@ -264,7 +264,8 @@ TEST_F("require that array attributes are updated", Fixture) { CollectionType ct(CollectionType::ARRAY); { - auto vec = AttributeBuilder("in1/aint", Config(BasicType::INT32, ct)).fill_array({{32}, {32}, {32}, {32}, {32}}).get(); + using IL = AttributeBuilder::IntList; + auto vec = AttributeBuilder("in1/aint", Config(BasicType::INT32, ct)).fill_array({IL{32}, {32}, {32}, {32}, {32}}).get(); auto first = std::make_unique<IntFieldValue>(32); auto second = std::make_unique<IntFieldValue>(64); auto assign = std::make_unique<ArrayFieldValue>(f.docType->getField("aint").getDataType()); @@ -279,7 +280,8 @@ TEST_F("require that array attributes are updated", Fixture) EXPECT_TRUE(check(vec, 5, std::vector<WeightedInt>{WeightedInt(32)})); } { - auto vec = AttributeBuilder("in1/afloat", Config(BasicType::FLOAT, ct)).fill_array({{55.5}, {55.5}, {55.5}, {55.5}, {55.5}}).get(); + using DL = AttributeBuilder::DoubleList; + auto vec = AttributeBuilder("in1/afloat", Config(BasicType::FLOAT, ct)).fill_array({DL{55.5}, {55.5}, {55.5}, {55.5}, {55.5}}).get(); auto first = std::make_unique<FloatFieldValue>(55.5f); auto second = std::make_unique<FloatFieldValue>(77.7f); auto assign = std::make_unique<ArrayFieldValue>(f.docType->getField("afloat").getDataType()); diff --git a/searchlib/src/tests/attribute/attribute_test.cpp b/searchlib/src/tests/attribute/attribute_test.cpp index 7eb43faac18..d2d3ccaad23 100644 --- a/searchlib/src/tests/attribute/attribute_test.cpp +++ b/searchlib/src/tests/attribute/attribute_test.cpp @@ -912,8 +912,8 @@ AttributeTest::testSingle() cfg.setFastSearch(true); AttributePtr ptr = createAttribute("sv-post-int32", cfg); ptr->updateStat(true); - EXPECT_EQ(339020u, ptr->getStatus().getAllocated()); - EXPECT_EQ(101852u, ptr->getStatus().getUsed()); + EXPECT_EQ(338972u, ptr->getStatus().getAllocated()); + EXPECT_EQ(101492u, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testSingle<IntegerAttribute, AttributeVector::largeint_t, int32_t>(ptr, values); } @@ -934,8 +934,8 @@ AttributeTest::testSingle() cfg.setFastSearch(true); AttributePtr ptr = createAttribute("sv-post-float", cfg); ptr->updateStat(true); - EXPECT_EQ(339020, ptr->getStatus().getAllocated()); - EXPECT_EQ(101852u, ptr->getStatus().getUsed()); + EXPECT_EQ(338972u, ptr->getStatus().getAllocated()); + EXPECT_EQ(101492u, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testSingle<FloatingPointAttribute, double, float>(ptr, values); } @@ -947,8 +947,8 @@ AttributeTest::testSingle() { AttributePtr ptr = createAttribute("sv-string", Config(BasicType::STRING, CollectionType::SINGLE)); ptr->updateStat(true); - EXPECT_EQ(117256u + sizeof_large_string_entry, ptr->getStatus().getAllocated()); - EXPECT_EQ(53240u + sizeof_large_string_entry, ptr->getStatus().getUsed()); + EXPECT_EQ(116528u + sizeof_large_string_entry, ptr->getStatus().getAllocated()); + EXPECT_EQ(52760u + sizeof_large_string_entry, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testSingle<StringAttribute, string, string>(ptr, values); } @@ -957,8 +957,8 @@ AttributeTest::testSingle() cfg.setFastSearch(true); AttributePtr ptr = createAttribute("sv-fs-string", cfg); ptr->updateStat(true); - EXPECT_EQ(345624u + sizeof_large_string_entry, ptr->getStatus().getAllocated()); - EXPECT_EQ(105176u + sizeof_large_string_entry, ptr->getStatus().getUsed()); + EXPECT_EQ(344848u + sizeof_large_string_entry, ptr->getStatus().getAllocated()); + EXPECT_EQ(104408u + sizeof_large_string_entry, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testSingle<StringAttribute, string, string>(ptr, values); } @@ -1089,8 +1089,8 @@ AttributeTest::testArray() { AttributePtr ptr = createAttribute("a-int32", Config(BasicType::INT32, CollectionType::ARRAY)); ptr->updateStat(true); - EXPECT_EQ(512056u, ptr->getStatus().getAllocated()); - EXPECT_EQ(504392u, ptr->getStatus().getUsed()); + EXPECT_EQ(487480u, ptr->getStatus().getAllocated()); + EXPECT_EQ(479720u, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testArray<IntegerAttribute, AttributeVector::largeint_t>(ptr, values); } @@ -1099,8 +1099,8 @@ AttributeTest::testArray() cfg.setFastSearch(true); AttributePtr ptr = createAttribute("flags", cfg); ptr->updateStat(true); - EXPECT_EQ(512056u, ptr->getStatus().getAllocated()); - EXPECT_EQ(504392u, ptr->getStatus().getUsed()); + EXPECT_EQ(487480u, ptr->getStatus().getAllocated()); + EXPECT_EQ(479720u, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testArray<IntegerAttribute, AttributeVector::largeint_t>(ptr, values); } @@ -1109,8 +1109,8 @@ AttributeTest::testArray() cfg.setFastSearch(true); AttributePtr ptr = createAttribute("a-fs-int32", cfg); ptr->updateStat(true); - EXPECT_EQ(868788u, ptr->getStatus().getAllocated()); - EXPECT_EQ(606264u, ptr->getStatus().getUsed()); + EXPECT_EQ(844116u, ptr->getStatus().getAllocated()); + EXPECT_EQ(581232u, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testArray<IntegerAttribute, AttributeVector::largeint_t>(ptr, values); } @@ -1128,8 +1128,8 @@ AttributeTest::testArray() cfg.setFastSearch(true); AttributePtr ptr = createAttribute("a-fs-float", cfg); ptr->updateStat(true); - EXPECT_EQ(868788u, ptr->getStatus().getAllocated()); - EXPECT_EQ(606264u, ptr->getStatus().getUsed()); + EXPECT_EQ(844116u, ptr->getStatus().getAllocated()); + EXPECT_EQ(581232u, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testArray<FloatingPointAttribute, double>(ptr, values); } @@ -1140,8 +1140,8 @@ AttributeTest::testArray() { AttributePtr ptr = createAttribute("a-string", Config(BasicType::STRING, CollectionType::ARRAY)); ptr->updateStat(true); - EXPECT_EQ(625088u + sizeof_large_string_entry, ptr->getStatus().getAllocated()); - EXPECT_EQ(557632u + sizeof_large_string_entry, ptr->getStatus().getUsed()); + EXPECT_EQ(599784u + sizeof_large_string_entry, ptr->getStatus().getAllocated()); + EXPECT_EQ(532480u + sizeof_large_string_entry, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testArray<StringAttribute, string>(ptr, values); } @@ -1150,8 +1150,8 @@ AttributeTest::testArray() cfg.setFastSearch(true); AttributePtr ptr = createAttribute("afs-string", cfg); ptr->updateStat(true); - EXPECT_EQ(875392u + sizeof_large_string_entry, ptr->getStatus().getAllocated()); - EXPECT_EQ(609588u + sizeof_large_string_entry, ptr->getStatus().getUsed()); + EXPECT_EQ(849992u + sizeof_large_string_entry, ptr->getStatus().getAllocated()); + EXPECT_EQ(584148u + sizeof_large_string_entry, ptr->getStatus().getUsed()); addDocs(ptr, numDocs); testArray<StringAttribute, string>(ptr, values); } @@ -2334,6 +2334,10 @@ AttributeTest::test_paged_attribute(const vespalib::string& name, const vespalib size_t rounded_size = vespalib::round_up_to_page_size(1); size_t lid_mapping_size = 1200; size_t sv_maxlid = 1200; + if (rounded_size == 16_Ki) { + lid_mapping_size = 4200; + sv_maxlid = 1300; + } if (rounded_size == 64_Ki) { lid_mapping_size = 17000; sv_maxlid = 1500; diff --git a/searchlib/src/tests/attribute/extendattributes/extendattribute.cpp b/searchlib/src/tests/attribute/extendattributes/extendattribute.cpp index 8f056323733..3f775e99891 100644 --- a/searchlib/src/tests/attribute/extendattributes/extendattribute.cpp +++ b/searchlib/src/tests/attribute/extendattributes/extendattribute.cpp @@ -1,9 +1,25 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/gtest/gtest.h> +#include <vespa/eval/eval/fast_value.h> +#include <vespa/eval/eval/tensor_spec.h> +#include <vespa/eval/eval/value.h> +#include <vespa/eval/eval/value_codec.h> +#include <vespa/searchcommon/attribute/config.h> #include <vespa/searchlib/attribute/extendableattributes.h> #include <vespa/searchlib/attribute/single_raw_ext_attribute.h> +#include <vespa/searchlib/tensor/tensor_ext_attribute.h> +#include <vespa/searchlib/tensor/vector_bundle.h> +#include <vespa/vespalib/stllike/asciistream.h> +using search::attribute::Config; +using search::attribute::BasicType; +using search::attribute::CollectionType; using search::attribute::SingleRawExtAttribute; +using search::tensor::TensorExtAttribute; +using vespalib::eval::FastValueBuilderFactory; +using vespalib::eval::TensorSpec; +using vespalib::eval::Value; +using vespalib::eval::ValueType; namespace search { @@ -15,8 +31,46 @@ std::vector<char> as_vector(vespalib::ConstArrayRef<char> value) { return {value.data(), value.data() + value.size()}; } +std::vector<double> as_vector(vespalib::ConstArrayRef<double> value) { + return {value.data(), value.data() + value.size()}; +} + +vespalib::string vec_2d_spec("tensor(x[2])"); +vespalib::string vec_mixed_2d_spec("tensor(a{},x[2])"); + +TensorSpec +vec_2d(double x0, double x1) +{ + return TensorSpec(vec_2d_spec).add({{"x", 0}}, x0).add({{"x", 1}}, x1); +} + +TensorSpec +vec_mixed_2d(std::vector<std::vector<double>> val) +{ + TensorSpec spec(vec_mixed_2d_spec); + for (uint32_t a = 0; a < val.size(); ++a) { + vespalib::asciistream a_stream; + a_stream << a; + vespalib::string a_as_string = a_stream.str(); + for (uint32_t x = 0; x < val[a].size(); ++x) { + spec.add({{"a", a_as_string.c_str()},{"x", x}}, val[a][x]); + } + } + return spec; +} + +void add_doc(AttributeVector& attr, uint32_t exp_docid) +{ + uint32_t docid(0); + EXPECT_EQ(exp_docid, attr.getNumDocs()); + attr.addDoc(docid); + EXPECT_EQ(exp_docid, docid); + EXPECT_EQ(exp_docid + 1, attr.getNumDocs()); +} + class ExtendAttributeTest : public ::testing::Test { + std::vector<std::unique_ptr<Value>> _tensors; protected: ExtendAttributeTest() = default; ~ExtendAttributeTest() override = default; @@ -27,16 +81,22 @@ protected: template <typename Attribute> void testExtendString(Attribute & attr); void testExtendRaw(AttributeVector& attr); + void testExtendTensor(AttributeVector& attr); + const Value& create_tensor(const TensorSpec &spec); }; +const Value& +ExtendAttributeTest::create_tensor(const TensorSpec &spec) +{ + auto value = value_from_spec(spec, FastValueBuilderFactory::get()); + _tensors.emplace_back(std::move(value)); + return *_tensors.back(); +} + template <typename Attribute> void ExtendAttributeTest::testExtendInteger(Attribute & attr) { - uint32_t docId(0); - EXPECT_EQ(attr.getNumDocs(), 0u); - attr.addDoc(docId); - EXPECT_EQ(docId, 0u); - EXPECT_EQ(attr.getNumDocs(), 1u); + add_doc(attr, 0); attr.add(1, 10); EXPECT_EQ(attr.getInt(0), 1); attr.add(2, 20); @@ -51,9 +111,7 @@ void ExtendAttributeTest::testExtendInteger(Attribute & attr) EXPECT_EQ(v[1].getWeight(), 20); } } - attr.addDoc(docId); - EXPECT_EQ(docId, 1u); - EXPECT_EQ(attr.getNumDocs(), 2u); + add_doc(attr, 1); attr.add(3, 30); EXPECT_EQ(attr.getInt(1), 3); if (attr.hasMultiValue()) { @@ -69,11 +127,7 @@ void ExtendAttributeTest::testExtendInteger(Attribute & attr) template <typename Attribute> void ExtendAttributeTest::testExtendFloat(Attribute & attr) { - uint32_t docId(0); - EXPECT_EQ(attr.getNumDocs(), 0u); - attr.addDoc(docId); - EXPECT_EQ(docId, 0u); - EXPECT_EQ(attr.getNumDocs(), 1u); + add_doc(attr, 0); attr.add(1.7, 10); EXPECT_EQ(attr.getInt(0), 1); EXPECT_EQ(attr.getFloat(0), 1.7); @@ -89,9 +143,7 @@ void ExtendAttributeTest::testExtendFloat(Attribute & attr) EXPECT_EQ(v[1].getWeight(), 20); } } - attr.addDoc(docId); - EXPECT_EQ(docId, 1u); - EXPECT_EQ(attr.getNumDocs(), 2u); + add_doc(attr, 1); attr.add(3.6, 30); EXPECT_EQ(attr.getFloat(1), 3.6); if (attr.hasMultiValue()) { @@ -107,11 +159,7 @@ void ExtendAttributeTest::testExtendFloat(Attribute & attr) template <typename Attribute> void ExtendAttributeTest::testExtendString(Attribute & attr) { - uint32_t docId(0); - EXPECT_EQ(attr.getNumDocs(), 0u); - attr.addDoc(docId); - EXPECT_EQ(docId, 0u); - EXPECT_EQ(attr.getNumDocs(), 1u); + add_doc(attr, 0); attr.add("1.7", 10); auto buf = attr.get_raw(0); EXPECT_EQ(std::string(buf.data(), buf.size()), "1.7"); @@ -128,9 +176,7 @@ void ExtendAttributeTest::testExtendString(Attribute & attr) EXPECT_EQ(v[1].getWeight(), 20); } } - attr.addDoc(docId); - EXPECT_EQ(docId, 1u); - EXPECT_EQ(attr.getNumDocs(), 2u); + add_doc(attr, 1); attr.add("3.6", 30); buf = attr.get_raw(1); EXPECT_EQ(std::string(buf.data(), buf.size()), "3.6"); @@ -150,41 +196,77 @@ void ExtendAttributeTest::testExtendRaw(AttributeVector& attr) std::vector<char> zeros{10, 0, 0, 11}; auto* ext_attr = attr.getExtendInterface(); EXPECT_NE(nullptr, ext_attr); - uint32_t docId(0); - EXPECT_EQ(0u, attr.getNumDocs()); - attr.addDoc(docId); - EXPECT_EQ(0u, docId); - EXPECT_EQ(1u, attr.getNumDocs()); + add_doc(attr, 0); ext_attr->add(as_vector("1.7")); auto buf = attr.get_raw(0); EXPECT_EQ(as_vector("1.7"), as_vector(buf)); ext_attr->add(vespalib::ConstArrayRef<char>(as_vector("2.3"))); buf = attr.get_raw(0); EXPECT_EQ(as_vector("2.3"), as_vector(buf)); - attr.addDoc(docId); - EXPECT_EQ(1u, docId); - EXPECT_EQ(attr.getNumDocs(), 2u); + add_doc(attr, 1); ext_attr->add(as_vector("3.6")); buf = attr.get_raw(1); EXPECT_EQ(as_vector("3.6"), as_vector(buf)); buf = attr.get_raw(0); EXPECT_EQ(as_vector("2.3"), as_vector(buf)); - attr.addDoc(docId); - EXPECT_EQ(2u, docId); + add_doc(attr, 2); ext_attr->add(zeros); buf = attr.get_raw(2); EXPECT_EQ(zeros, as_vector(buf)); - attr.addDoc(docId); - EXPECT_EQ(3u, docId); + add_doc(attr, 3); buf = attr.get_raw(3); EXPECT_EQ(empty, as_vector(buf)); - attr.addDoc(docId); - EXPECT_EQ(4u, docId); + add_doc(attr, 4); ext_attr->add(empty); buf = attr.get_raw(4); EXPECT_EQ(empty, as_vector(buf)); } +void ExtendAttributeTest::testExtendTensor(AttributeVector& attr) +{ + std::vector<double> empty_cells{0.0, 0.0}; + std::vector<double> spec0_dense_cells{1.0, 2.0}; + std::vector<double> spec0_mixed_cells0{3.0, 4.0}; + std::vector<double> spec0_mixed_cells1{5.0, 6.0}; + bool dense = attr.getConfig().tensorType().is_dense(); + auto* ext_attr = attr.getExtendInterface(); + EXPECT_NE(nullptr, ext_attr); + auto* tensor_attr = attr.asTensorAttribute(); + EXPECT_NE(nullptr, tensor_attr); + add_doc(attr, 0); + TensorSpec spec0 = dense ? vec_2d(1.0, 2.0) : vec_mixed_2d({{3.0, 4.0}, {5.0, 6.0}}); + EXPECT_TRUE(ext_attr->add(create_tensor(spec0))); + auto tensor = tensor_attr->getTensor(0); + EXPECT_NE(nullptr, tensor.get()); + EXPECT_EQ(spec0, TensorSpec::from_value(*tensor)); + EXPECT_EQ(dense, tensor_attr->supports_extract_cells_ref()); + if (dense) { + EXPECT_EQ(spec0_dense_cells, as_vector(tensor_attr->extract_cells_ref(0).typify<double>())); + } + EXPECT_TRUE(tensor_attr->supports_get_tensor_ref()); + EXPECT_EQ(spec0, TensorSpec::from_value(tensor_attr->get_tensor_ref(0))); + EXPECT_FALSE(tensor_attr->supports_get_serialized_tensor_ref()); + auto vectors = tensor_attr->get_vectors(0); + if (dense) { + EXPECT_EQ(1, vectors.subspaces()); + EXPECT_EQ(spec0_dense_cells, as_vector(vectors.cells(0).typify<double>())); + EXPECT_EQ(spec0_dense_cells, as_vector(tensor_attr->get_vector(0, 0).typify<double>())); + EXPECT_EQ(empty_cells, as_vector(tensor_attr->get_vector(0, 1).typify<double>())); + } else { + EXPECT_EQ(2, vectors.subspaces()); + EXPECT_EQ(spec0_mixed_cells0, as_vector(vectors.cells(0).typify<double>())); + EXPECT_EQ(spec0_mixed_cells1, as_vector(vectors.cells(1).typify<double>())); + EXPECT_EQ(spec0_mixed_cells0, as_vector(tensor_attr->get_vector(0, 0).typify<double>())); + EXPECT_EQ(spec0_mixed_cells1, as_vector(tensor_attr->get_vector(0, 1).typify<double>())); + EXPECT_EQ(empty_cells, as_vector(tensor_attr->get_vector(0, 2).typify<double>())); + } + add_doc(attr, 1); + vectors = tensor_attr->get_vectors(1); + EXPECT_EQ(0, vectors.subspaces()); + EXPECT_EQ(empty_cells, as_vector(tensor_attr->get_vector(1, 0).typify<double>())); + EXPECT_EQ(nullptr, tensor_attr->getTensor(1).get()); +} + TEST_F(ExtendAttributeTest, single_integer_ext_attribute) { SingleIntegerExtAttribute siattr("si1"); @@ -255,6 +337,24 @@ TEST_F(ExtendAttributeTest, single_raw_ext_attribute) testExtendRaw(srattr); } +TEST_F(ExtendAttributeTest, tensor_ext_attribute_dense) +{ + Config cfg(BasicType::TENSOR, CollectionType::SINGLE); + cfg.setTensorType(ValueType::from_spec(vec_2d_spec)); + TensorExtAttribute tattr("td1", cfg); + EXPECT_TRUE(! tattr.hasMultiValue()); + testExtendTensor(tattr); +} + +TEST_F(ExtendAttributeTest, tensor_ext_attribute_mixed) +{ + Config cfg(BasicType::TENSOR, CollectionType::SINGLE); + cfg.setTensorType(ValueType::from_spec(vec_mixed_2d_spec)); + TensorExtAttribute tattr("tm1", cfg); + EXPECT_TRUE(! tattr.hasMultiValue()); + testExtendTensor(tattr); +} + } GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/attribute/multi_value_mapping/multi_value_mapping_test.cpp b/searchlib/src/tests/attribute/multi_value_mapping/multi_value_mapping_test.cpp index df2a5ea2ac9..17e852c8b92 100644 --- a/searchlib/src/tests/attribute/multi_value_mapping/multi_value_mapping_test.cpp +++ b/searchlib/src/tests/attribute/multi_value_mapping/multi_value_mapping_test.cpp @@ -22,11 +22,11 @@ using vespalib::datastore::CompactionStrategy; using vespalib::alloc::test::MemoryAllocatorObserver; using AllocStats = MemoryAllocatorObserver::Stats; -template <typename EntryT> +template <typename ElemT> void -assertArray(const std::vector<EntryT> &exp, vespalib::ConstArrayRef<EntryT> values) +assertArray(const std::vector<ElemT> &exp, vespalib::ConstArrayRef<ElemT> values) { - EXPECT_EQ(exp, std::vector<EntryT>(values.cbegin(), values.cend())); + EXPECT_EQ(exp, std::vector<ElemT>(values.cbegin(), values.cend())); } template <class MvMapping> @@ -69,10 +69,10 @@ public: constexpr float ALLOC_GROW_FACTOR = 0.2; -template <typename EntryT> +template <typename ElemT> class MappingTestBase : public ::testing::Test { protected: - using MvMapping = search::attribute::MultiValueMapping<EntryT>; + using MvMapping = search::attribute::MultiValueMapping<ElemT>; using AttributeType = MyAttribute<MvMapping>; AllocStats _stats; std::unique_ptr<MvMapping> _mvMapping; @@ -82,8 +82,8 @@ protected: using generation_t = vespalib::GenerationHandler::generation_t; public: - using ArrayRef = vespalib::ArrayRef<EntryT>; - using ConstArrayRef = vespalib::ConstArrayRef<EntryT>; + using ArrayRef = vespalib::ArrayRef<ElemT>; + using ConstArrayRef = vespalib::ConstArrayRef<ElemT>; MappingTestBase() : _stats(), _mvMapping(), @@ -99,9 +99,9 @@ public: _attr = std::make_unique<AttributeType>(*_mvMapping); _maxSmallArraySize = maxSmallArraySize; } - void setup(uint32_t maxSmallArraySize, size_t minArrays, size_t maxArrays, size_t numArraysForNewBuffer, bool enable_free_lists = true) { + void setup(uint32_t maxSmallArraySize, size_t min_entries, size_t max_entries, size_t num_entries_for_new_buffer, bool enable_free_lists = true) { ArrayStoreConfig config(maxSmallArraySize, - ArrayStoreConfig::AllocSpec(minArrays, maxArrays, numArraysForNewBuffer, ALLOC_GROW_FACTOR)); + ArrayStoreConfig::AllocSpec(min_entries, max_entries, num_entries_for_new_buffer, ALLOC_GROW_FACTOR)); config.enable_free_lists(enable_free_lists); _mvMapping = std::make_unique<MvMapping>(config, vespalib::GrowStrategy(), std::make_unique<MemoryAllocatorObserver>(_stats)); _attr = std::make_unique<AttributeType>(*_mvMapping); @@ -109,12 +109,12 @@ public: } ~MappingTestBase() { } - void set(uint32_t docId, const std::vector<EntryT> &values) { _mvMapping->set(docId, values); } + void set(uint32_t docId, const std::vector<ElemT> &values) { _mvMapping->set(docId, values); } ConstArrayRef get(uint32_t docId) { return _mvMapping->get(docId); } ArrayRef get_writable(uint32_t docId) { return _mvMapping->get_writable(docId); } - void assertGet(uint32_t docId, const std::vector<EntryT> &exp) { + void assertGet(uint32_t docId, const std::vector<ElemT> &exp) { ConstArrayRef act = get(docId); - EXPECT_EQ(exp, std::vector<EntryT>(act.cbegin(), act.cend())); + EXPECT_EQ(exp, std::vector<ElemT>(act.cbegin(), act.cend())); } void assign_generation(generation_t current_gen) { _mvMapping->assign_generation(current_gen); } void reclaim_memory(generation_t oldest_used_gen) { _mvMapping->reclaim_memory(oldest_used_gen); } diff --git a/searchlib/src/tests/memoryindex/field_index/field_index_test.cpp b/searchlib/src/tests/memoryindex/field_index/field_index_test.cpp index b57a2f42ea7..69478c09a25 100644 --- a/searchlib/src/tests/memoryindex/field_index/field_index_test.cpp +++ b/searchlib/src/tests/memoryindex/field_index/field_index_test.cpp @@ -1032,14 +1032,14 @@ TEST_F(BasicInverterTest, require_that_inversion_is_working) auto beforeStats = getFeatureStoreMemStats(_fic); LOG(info, - "Before feature compaction: allocElems=%zu, usedElems=%zu" - ", deadElems=%zu, holdElems=%zu" + "Before feature compaction: alloc_entries=%zu, used_entries=%zu" + ", dead_entries=%zu, hold_entries=%zu" ", freeBuffers=%u, activeBuffers=%u" ", holdBuffers=%u", - beforeStats._allocElems, - beforeStats._usedElems, - beforeStats._deadElems, - beforeStats._holdElems, + beforeStats._alloc_entries, + beforeStats._used_entries, + beforeStats._dead_entries, + beforeStats._hold_entries, beforeStats._freeBuffers, beforeStats._activeBuffers, beforeStats._holdBuffers); @@ -1052,14 +1052,14 @@ TEST_F(BasicInverterTest, require_that_inversion_is_working) myCommit(_fic, *_pushThreads); auto duringStats = getFeatureStoreMemStats(_fic); LOG(info, - "During feature compaction: allocElems=%zu, usedElems=%zu" - ", deadElems=%zu, holdElems=%zu" + "During feature compaction: alloc_entries=%zu, used_entries=%zu" + ", dead_entries=%zu, hold_entries=%zu" ", freeBuffers=%u, activeBuffers=%u" ", holdBuffers=%u", - duringStats._allocElems, - duringStats._usedElems, - duringStats._deadElems, - duringStats._holdElems, + duringStats._alloc_entries, + duringStats._used_entries, + duringStats._dead_entries, + duringStats._hold_entries, duringStats._freeBuffers, duringStats._activeBuffers, duringStats._holdBuffers); @@ -1067,14 +1067,14 @@ TEST_F(BasicInverterTest, require_that_inversion_is_working) myCommit(_fic, *_pushThreads); auto afterStats = getFeatureStoreMemStats(_fic); LOG(info, - "After feature compaction: allocElems=%zu, usedElems=%zu" - ", deadElems=%zu, holdElems=%zu" + "After feature compaction: alloc_entries=%zu, used_entries=%zu" + ", dead_entries=%zu, hold_entries=%zu" ", freeBuffers=%u, activeBuffers=%u" ", holdBuffers=%u", - afterStats._allocElems, - afterStats._usedElems, - afterStats._deadElems, - afterStats._holdElems, + afterStats._alloc_entries, + afterStats._used_entries, + afterStats._dead_entries, + afterStats._hold_entries, afterStats._freeBuffers, afterStats._activeBuffers, afterStats._holdBuffers); diff --git a/searchlib/src/tests/memoryindex/memory_index/memory_index_test.cpp b/searchlib/src/tests/memoryindex/memory_index/memory_index_test.cpp index 8073fb8d232..e3b9cf9702d 100644 --- a/searchlib/src/tests/memoryindex/memory_index/memory_index_test.cpp +++ b/searchlib/src/tests/memoryindex/memory_index/memory_index_test.cpp @@ -462,8 +462,8 @@ TEST(MemoryIndexTest, require_that_num_docs_and_doc_id_limit_is_returned) TEST(MemoryIndexTest, require_that_we_understand_the_memory_footprint) { - constexpr size_t BASE_ALLOCATED = 361032u; - constexpr size_t BASE_USED = 151188u; + constexpr size_t BASE_ALLOCATED = 360936u; + constexpr size_t BASE_USED = 150804u; { MySetup setup; Index index(setup); diff --git a/searchlib/src/tests/predicate/document_features_store_test.cpp b/searchlib/src/tests/predicate/document_features_store_test.cpp index 4ac4bdc32f0..d30df9dba6e 100644 --- a/searchlib/src/tests/predicate/document_features_store_test.cpp +++ b/searchlib/src/tests/predicate/document_features_store_test.cpp @@ -165,17 +165,17 @@ TEST("require that both features and ranges are removed by 'remove'") { TEST("require that both features and ranges counts towards memory usage") { DocumentFeaturesStore features_store(10); - EXPECT_EQUAL(50136u, features_store.getMemoryUsage().usedBytes()); + EXPECT_EQUAL(50064u, features_store.getMemoryUsage().usedBytes()); PredicateTreeAnnotations annotations; annotations.features.push_back(PredicateHash::hash64("foo=100-199")); features_store.insert(annotations, doc_id); - EXPECT_EQUAL(50144u, features_store.getMemoryUsage().usedBytes()); + EXPECT_EQUAL(50072u, features_store.getMemoryUsage().usedBytes()); annotations.features.clear(); annotations.range_features.push_back({"foo", 100, 199}); features_store.insert(annotations, doc_id + 1); - EXPECT_EQUAL(50240u, features_store.getMemoryUsage().usedBytes()); + EXPECT_EQUAL(50168u, features_store.getMemoryUsage().usedBytes()); } TEST("require that DocumentFeaturesStore can be serialized") { @@ -205,17 +205,17 @@ TEST("require that serialization cleans up wordstore") { PredicateTreeAnnotations annotations; annotations.range_features.push_back({"foo", 100, 199}); features_store.insert(annotations, doc_id); - EXPECT_EQUAL(50232u, features_store.getMemoryUsage().usedBytes()); + EXPECT_EQUAL(50160u, features_store.getMemoryUsage().usedBytes()); annotations.range_features.push_back({"bar", 100, 199}); features_store.insert(annotations, doc_id + 1); - EXPECT_EQUAL(50620u, features_store.getMemoryUsage().usedBytes()); + EXPECT_EQUAL(50548u, features_store.getMemoryUsage().usedBytes()); features_store.remove(doc_id + 1); - EXPECT_EQUAL(50572u, features_store.getMemoryUsage().usedBytes()); + EXPECT_EQUAL(50500u, features_store.getMemoryUsage().usedBytes()); vespalib::DataBuffer buffer; features_store.serialize(buffer); DocumentFeaturesStore features_store2(buffer); - EXPECT_EQUAL(50232u, features_store2.getMemoryUsage().usedBytes()); + EXPECT_EQUAL(50160u, features_store2.getMemoryUsage().usedBytes()); } diff --git a/searchlib/src/vespa/searchlib/attribute/attributevector.h b/searchlib/src/vespa/searchlib/attribute/attributevector.h index 3d14622ca02..e40785911ea 100644 --- a/searchlib/src/vespa/searchlib/attribute/attributevector.h +++ b/searchlib/src/vespa/searchlib/attribute/attributevector.h @@ -38,6 +38,8 @@ namespace vespalib::alloc { class Alloc; } +namespace vespalib::eval { struct Value; } + namespace search { template <typename T> class ComponentGuard; @@ -86,6 +88,7 @@ public: virtual bool add(double, int32_t = 1) { return false; } virtual bool add(const char *, int32_t = 1) { return false; } virtual bool add(vespalib::ConstArrayRef<char>, int32_t = 1) { return false; } + virtual bool add(const vespalib::eval::Value&, int32_t = 1) { return false; } virtual ~IExtendAttribute() = default; }; diff --git a/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.h b/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.h index bd624e9f388..4fce64aa762 100644 --- a/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.h +++ b/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.h @@ -12,18 +12,18 @@ namespace search::attribute { /** * Class for mapping from document id to an array of values. */ -template <typename EntryT, typename RefT = vespalib::datastore::EntryRefT<19> > +template <typename ElemT, typename RefT = vespalib::datastore::EntryRefT<19> > class MultiValueMapping : public MultiValueMappingBase { public: - using MultiValueType = EntryT; + using MultiValueType = ElemT; using RefType = RefT; - using ReadView = MultiValueMappingReadView<EntryT, RefT>; + using ReadView = MultiValueMappingReadView<ElemT, RefT>; private: - using ArrayRef = vespalib::ArrayRef<EntryT>; - using ArrayStore = vespalib::datastore::ArrayStore<EntryT, RefT>; + using ArrayRef = vespalib::ArrayRef<ElemT>; + using ArrayStore = vespalib::datastore::ArrayStore<ElemT, RefT>; using generation_t = vespalib::GenerationHandler::generation_t; - using ConstArrayRef = vespalib::ConstArrayRef<EntryT>; + using ConstArrayRef = vespalib::ConstArrayRef<ElemT>; ArrayStore _store; public: @@ -73,7 +73,7 @@ public: static vespalib::datastore::ArrayStoreConfig optimizedConfigForHugePage(size_t maxSmallArraySize, size_t hugePageSize, size_t smallPageSize, - size_t minNumArraysForNewBuffer, + size_t min_num_entries_for_new_buffer, float allocGrowFactor, bool enable_free_lists); }; diff --git a/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.hpp b/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.hpp index b486fa60265..ab68bea58cc 100644 --- a/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.hpp +++ b/searchlib/src/vespa/searchlib/attribute/multi_value_mapping.hpp @@ -7,8 +7,8 @@ namespace search::attribute { -template <typename EntryT, typename RefT> -MultiValueMapping<EntryT,RefT>::MultiValueMapping(const vespalib::datastore::ArrayStoreConfig &storeCfg, +template <typename ElemT, typename RefT> +MultiValueMapping<ElemT,RefT>::MultiValueMapping(const vespalib::datastore::ArrayStoreConfig &storeCfg, const vespalib::GrowStrategy &gs, std::shared_ptr<vespalib::alloc::MemoryAllocator> memory_allocator) : MultiValueMappingBase(gs, ArrayStore::getGenerationHolderLocation(_store), memory_allocator), @@ -16,12 +16,12 @@ MultiValueMapping<EntryT,RefT>::MultiValueMapping(const vespalib::datastore::Arr { } -template <typename EntryT, typename RefT> -MultiValueMapping<EntryT,RefT>::~MultiValueMapping() = default; +template <typename ElemT, typename RefT> +MultiValueMapping<ElemT,RefT>::~MultiValueMapping() = default; -template <typename EntryT, typename RefT> +template <typename ElemT, typename RefT> void -MultiValueMapping<EntryT,RefT>::set(uint32_t docId, ConstArrayRef values) +MultiValueMapping<ElemT,RefT>::set(uint32_t docId, ConstArrayRef values) { _indices.ensure_size(docId + 1); EntryRef oldRef(_indices[docId].load_relaxed()); @@ -31,18 +31,18 @@ MultiValueMapping<EntryT,RefT>::set(uint32_t docId, ConstArrayRef values) _store.remove(oldRef); } -template <typename EntryT, typename RefT> +template <typename ElemT, typename RefT> vespalib::MemoryUsage -MultiValueMapping<EntryT,RefT>::update_stat(const CompactionStrategy& compaction_strategy) +MultiValueMapping<ElemT,RefT>::update_stat(const CompactionStrategy& compaction_strategy) { auto retval = _store.update_stat(compaction_strategy); retval.merge(_indices.getMemoryUsage()); return retval; } -template <typename EntryT, typename RefT> +template <typename ElemT, typename RefT> void -MultiValueMapping<EntryT,RefT>::compact_worst(const CompactionStrategy& compaction_strategy) +MultiValueMapping<ElemT,RefT>::compact_worst(const CompactionStrategy& compaction_strategy) { vespalib::datastore::ICompactionContext::UP compactionContext(_store.compact_worst(compaction_strategy)); if (compactionContext) { @@ -50,29 +50,29 @@ MultiValueMapping<EntryT,RefT>::compact_worst(const CompactionStrategy& compacti } } -template <typename EntryT, typename RefT> +template <typename ElemT, typename RefT> vespalib::MemoryUsage -MultiValueMapping<EntryT,RefT>::getArrayStoreMemoryUsage() const +MultiValueMapping<ElemT,RefT>::getArrayStoreMemoryUsage() const { return _store.getMemoryUsage(); } -template <typename EntryT, typename RefT> +template <typename ElemT, typename RefT> vespalib::AddressSpace -MultiValueMapping<EntryT, RefT>::getAddressSpaceUsage() const { +MultiValueMapping<ElemT, RefT>::getAddressSpaceUsage() const { return _store.addressSpaceUsage(); } -template <typename EntryT, typename RefT> +template <typename ElemT, typename RefT> vespalib::datastore::ArrayStoreConfig -MultiValueMapping<EntryT, RefT>::optimizedConfigForHugePage(size_t maxSmallArraySize, +MultiValueMapping<ElemT, RefT>::optimizedConfigForHugePage(size_t maxSmallArraySize, size_t hugePageSize, size_t smallPageSize, - size_t minNumArraysForNewBuffer, + size_t min_num_entries_for_new_buffer, float allocGrowFactor, bool enable_free_lists) { - auto result = ArrayStore::optimizedConfigForHugePage(maxSmallArraySize, hugePageSize, smallPageSize, minNumArraysForNewBuffer, allocGrowFactor); + auto result = ArrayStore::optimizedConfigForHugePage(maxSmallArraySize, hugePageSize, smallPageSize, min_num_entries_for_new_buffer, allocGrowFactor); result.enable_free_lists(enable_free_lists); return result; } diff --git a/searchlib/src/vespa/searchlib/attribute/multi_value_mapping_read_view.h b/searchlib/src/vespa/searchlib/attribute/multi_value_mapping_read_view.h index 116e069e8b4..41138ff0890 100644 --- a/searchlib/src/vespa/searchlib/attribute/multi_value_mapping_read_view.h +++ b/searchlib/src/vespa/searchlib/attribute/multi_value_mapping_read_view.h @@ -11,12 +11,12 @@ namespace search::attribute { /** * Class for mapping from document id to an array of values as reader. */ -template <typename EntryT, typename RefT = vespalib::datastore::EntryRefT<19> > +template <typename ElemT, typename RefT = vespalib::datastore::EntryRefT<19> > class MultiValueMappingReadView { using AtomicEntryRef = vespalib::datastore::AtomicEntryRef; using Indices = vespalib::ConstArrayRef<AtomicEntryRef>; - using ArrayStore = vespalib::datastore::ArrayStore<EntryT, RefT>; + using ArrayStore = vespalib::datastore::ArrayStore<ElemT, RefT>; Indices _indices; const ArrayStore* _store; @@ -31,7 +31,7 @@ public: _store(store) { } - vespalib::ConstArrayRef<EntryT> get(uint32_t doc_id) const { return _store->get(_indices[doc_id].load_acquire()); } + vespalib::ConstArrayRef<ElemT> get(uint32_t doc_id) const { return _store->get(_indices[doc_id].load_acquire()); } bool valid() const noexcept { return _store != nullptr; } }; diff --git a/searchlib/src/vespa/searchlib/attribute/multinumericpostattribute.hpp b/searchlib/src/vespa/searchlib/attribute/multinumericpostattribute.hpp index ea449300aef..1009fa2fb5f 100644 --- a/searchlib/src/vespa/searchlib/attribute/multinumericpostattribute.hpp +++ b/searchlib/src/vespa/searchlib/attribute/multinumericpostattribute.hpp @@ -51,7 +51,7 @@ template <typename B, typename M> MultiValueNumericPostingAttribute<B, M>::~MultiValueNumericPostingAttribute() { this->disableFreeLists(); - this->disableElemHoldList(); + this->disable_entry_hold_list(); clearAllPostings(); } diff --git a/searchlib/src/vespa/searchlib/attribute/multistringpostattribute.hpp b/searchlib/src/vespa/searchlib/attribute/multistringpostattribute.hpp index cd46bbb5a8a..19840b5a474 100644 --- a/searchlib/src/vespa/searchlib/attribute/multistringpostattribute.hpp +++ b/searchlib/src/vespa/searchlib/attribute/multistringpostattribute.hpp @@ -27,7 +27,7 @@ template <typename B, typename T> MultiValueStringPostingAttributeT<B, T>::~MultiValueStringPostingAttributeT() { this->disableFreeLists(); - this->disableElemHoldList(); + this->disable_entry_hold_list(); clearAllPostings(); } diff --git a/searchlib/src/vespa/searchlib/attribute/postinglistattribute.h b/searchlib/src/vespa/searchlib/attribute/postinglistattribute.h index 29440b6ce43..ecf7a46f21e 100644 --- a/searchlib/src/vespa/searchlib/attribute/postinglistattribute.h +++ b/searchlib/src/vespa/searchlib/attribute/postinglistattribute.h @@ -58,7 +58,7 @@ protected: void updatePostings(PostingMap &changePost, const vespalib::datastore::EntryComparator &cmp); void clearAllPostings(); void disableFreeLists() { _postingList.disableFreeLists(); } - void disableElemHoldList() { _postingList.disableElemHoldList(); } + void disable_entry_hold_list() { _postingList.disable_entry_hold_list(); } void handle_load_posting_lists_and_update_enum_store(enumstore::EnumeratedPostingsLoader& loader); bool forwardedOnAddDoc(DocId doc, size_t wantSize, size_t wantCapacity); diff --git a/searchlib/src/vespa/searchlib/attribute/postingstore.cpp b/searchlib/src/vespa/searchlib/attribute/postingstore.cpp index 94720212faf..2703201b292 100644 --- a/searchlib/src/vespa/searchlib/attribute/postingstore.cpp +++ b/searchlib/src/vespa/searchlib/attribute/postingstore.cpp @@ -231,7 +231,7 @@ PostingStore<DataT>::dropBitVector(EntryRef &ref) (void) tree; (void) docFreq; _bvs.erase(ref.ref()); - _store.holdElem(iRef, 1); + _store.hold_entry(iRef); _status.decBitVectors(); _bvExtraBytes -= bv->writer().extraByteSize(); ref = ref2; @@ -267,7 +267,7 @@ PostingStore<DataT>::makeBitVector(EntryRef &ref) if (_enableOnlyBitVector) { BTreeType *tree = getWTreeEntry(iRef); tree->clear(_allocator); - _store.holdElem(ref, 1); + _store.hold_entry(ref); } else { bve->_tree = ref; } @@ -590,19 +590,19 @@ PostingStore<DataT>::clear(const EntryRef ref) assert(isBTree(iRef2)); BTreeType *tree = getWTreeEntry(iRef2); tree->clear(_allocator); - _store.holdElem(iRef2, 1); + _store.hold_entry(iRef2); } _bvs.erase(ref.ref()); _status.decBitVectors(); _bvExtraBytes -= bve->_bv->writer().extraByteSize(); - _store.holdElem(ref, 1); + _store.hold_entry(ref); } else { BTreeType *tree = getWTreeEntry(iRef); tree->clear(_allocator); - _store.holdElem(ref, 1); + _store.hold_entry(ref); } } else { - _store.holdElem(ref, clusterSize); + _store.hold_entry(ref); } } diff --git a/searchlib/src/vespa/searchlib/attribute/singlenumericpostattribute.hpp b/searchlib/src/vespa/searchlib/attribute/singlenumericpostattribute.hpp index 1775774171d..de4a7157dae 100644 --- a/searchlib/src/vespa/searchlib/attribute/singlenumericpostattribute.hpp +++ b/searchlib/src/vespa/searchlib/attribute/singlenumericpostattribute.hpp @@ -13,7 +13,7 @@ template <typename B> SingleValueNumericPostingAttribute<B>::~SingleValueNumericPostingAttribute() { this->disableFreeLists(); - this->disableElemHoldList(); + this->disable_entry_hold_list(); clearAllPostings(); } diff --git a/searchlib/src/vespa/searchlib/attribute/singlestringpostattribute.hpp b/searchlib/src/vespa/searchlib/attribute/singlestringpostattribute.hpp index eef72984e79..1ec9b54a73b 100644 --- a/searchlib/src/vespa/searchlib/attribute/singlestringpostattribute.hpp +++ b/searchlib/src/vespa/searchlib/attribute/singlestringpostattribute.hpp @@ -27,7 +27,7 @@ template <typename B> SingleValueStringPostingAttributeT<B>::~SingleValueStringPostingAttributeT() { this->disableFreeLists(); - this->disableElemHoldList(); + this->disable_entry_hold_list(); clearAllPostings(); } diff --git a/searchlib/src/vespa/searchlib/memoryindex/feature_store.cpp b/searchlib/src/vespa/searchlib/memoryindex/feature_store.cpp index 72f4a7ae579..4bc7f5b1144 100644 --- a/searchlib/src/vespa/searchlib/memoryindex/feature_store.cpp +++ b/searchlib/src/vespa/searchlib/memoryindex/feature_store.cpp @@ -36,7 +36,7 @@ EntryRef FeatureStore::addFeatures(const uint8_t *src, uint64_t byteLen) { uint32_t pad = Aligner::pad(byteLen); - auto result = _store.rawAllocator<uint8_t>(_typeId).alloc(byteLen + pad, DECODE_SAFETY); + auto result = _store.rawAllocator<uint8_t>(_typeId).alloc((byteLen + pad) / buffer_array_size, DECODE_SAFETY_ENTRIES); uint8_t *dst = result.data; memcpy(dst, src, byteLen); dst += byteLen; @@ -113,7 +113,7 @@ FeatureStore::add_features_guard_bytes() { uint32_t len = DECODE_SAFETY; uint32_t pad = Aligner::pad(len); - auto result = _store.rawAllocator<uint8_t>(_typeId).alloc(len + pad); + auto result = _store.rawAllocator<uint8_t>(_typeId).alloc((len + pad) / buffer_array_size); memset(result.data, 0, len + pad); } diff --git a/searchlib/src/vespa/searchlib/memoryindex/feature_store.h b/searchlib/src/vespa/searchlib/memoryindex/feature_store.h index 53588fa2894..1e48189987e 100644 --- a/searchlib/src/vespa/searchlib/memoryindex/feature_store.h +++ b/searchlib/src/vespa/searchlib/memoryindex/feature_store.h @@ -29,6 +29,7 @@ private: using PosOccFieldsParams = bitcompression::PosOccFieldsParams; static constexpr uint32_t DECODE_SAFETY = 16; + static constexpr uint32_t DECODE_SAFETY_ENTRIES = 16 / buffer_array_size; DataStoreType _store; @@ -117,7 +118,7 @@ public: * overrun beyond the compressed data either goes into other features * already written or into the guard area. * - * If buffer type is changed to have a nonzero numArraysForNewBuffer then + * If buffer type is changed to have a nonzero num_entries_for_new_buffer then * extra logic to add guard bytes is needed when switching primary buffer * to avoid issues if the buffer is resumed as primary buffer later on. */ diff --git a/searchlib/src/vespa/searchlib/memoryindex/field_index.cpp b/searchlib/src/vespa/searchlib/memoryindex/field_index.cpp index 4be3031303e..8dd76a90b14 100644 --- a/searchlib/src/vespa/searchlib/memoryindex/field_index.cpp +++ b/searchlib/src/vespa/searchlib/memoryindex/field_index.cpp @@ -55,9 +55,9 @@ template <bool interleaved_features> FieldIndex<interleaved_features>::~FieldIndex() { _postingListStore.disableFreeLists(); - _postingListStore.disableElemHoldList(); + _postingListStore.disable_entry_hold_list(); _dict.disableFreeLists(); - _dict.disableElemHoldList(); + _dict.disable_entry_hold_list(); // XXX: Kludge for (DictionaryTree::Iterator it = _dict.begin(); it.valid(); ++it) { diff --git a/searchlib/src/vespa/searchlib/memoryindex/word_store.cpp b/searchlib/src/vespa/searchlib/memoryindex/word_store.cpp index 3e4c38ceb0e..e330dc83055 100644 --- a/searchlib/src/vespa/searchlib/memoryindex/word_store.cpp +++ b/searchlib/src/vespa/searchlib/memoryindex/word_store.cpp @@ -27,7 +27,7 @@ WordStore::addWord(const vespalib::stringref word) { size_t wordSize = word.size() + 1; size_t bufferSize = wordSize + Aligner::pad(wordSize); - auto result = _store.rawAllocator<char>(_typeId).alloc(bufferSize); + auto result = _store.rawAllocator<char>(_typeId).alloc(bufferSize / buffer_array_size); char *be = result.data; for (size_t i = 0; i < word.size(); ++i) { *be++ = word[i]; diff --git a/searchlib/src/vespa/searchlib/predicate/document_features_store.cpp b/searchlib/src/vespa/searchlib/predicate/document_features_store.cpp index 604a467a6e6..a6a82ec09f8 100644 --- a/searchlib/src/vespa/searchlib/predicate/document_features_store.cpp +++ b/searchlib/src/vespa/searchlib/predicate/document_features_store.cpp @@ -102,7 +102,7 @@ DocumentFeaturesStore::DocumentFeaturesStore(DataBuffer &buffer) DocumentFeaturesStore::~DocumentFeaturesStore() { _word_index.disableFreeLists(); - _word_index.disableElemHoldList(); + _word_index.disable_entry_hold_list(); _word_index.getAllocator().freeze(); _word_index.clear(); } diff --git a/searchlib/src/vespa/searchlib/predicate/predicate_interval_store.cpp b/searchlib/src/vespa/searchlib/predicate/predicate_interval_store.cpp index af809b2fa69..af5aae6e519 100644 --- a/searchlib/src/vespa/searchlib/predicate/predicate_interval_store.cpp +++ b/searchlib/src/vespa/searchlib/predicate/predicate_interval_store.cpp @@ -95,7 +95,7 @@ PredicateIntervalStore::remove(EntryRef ref) { // BufferState &state = _store.getBufferState(buffer_id); // uint32_t type_id = state.getTypeId(); // uint32_t size = type_id <= MAX_ARRAY_SIZE ? type_id : 1; - // _store.holdElem(ref, size); + // _store.hold_entries(ref, size); } } diff --git a/searchlib/src/vespa/searchlib/predicate/simple_index.hpp b/searchlib/src/vespa/searchlib/predicate/simple_index.hpp index c6f640d72ed..9320488f88e 100644 --- a/searchlib/src/vespa/searchlib/predicate/simple_index.hpp +++ b/searchlib/src/vespa/searchlib/predicate/simple_index.hpp @@ -41,7 +41,7 @@ SimpleIndex<Posting, Key, DocId>::insertIntoVectorPosting(vespalib::datastore::E template <typename Posting, typename Key, typename DocId> SimpleIndex<Posting, Key, DocId>::~SimpleIndex() { _btree_posting_lists.disableFreeLists(); - _btree_posting_lists.disableElemHoldList(); + _btree_posting_lists.disable_entry_hold_list(); for (auto it = _dictionary.begin(); it.valid(); ++it) { vespalib::datastore::EntryRef ref(it.getData()); @@ -51,13 +51,13 @@ SimpleIndex<Posting, Key, DocId>::~SimpleIndex() { } _vector_posting_lists.disableFreeLists(); - _vector_posting_lists.disableElemHoldList(); + _vector_posting_lists.disable_entry_hold_list(); _vector_posting_lists.clear(); _vector_posting_lists.getAllocator().freeze(); _vector_posting_lists.getAllocator().reclaim_all_memory(); _dictionary.disableFreeLists(); - _dictionary.disableElemHoldList(); + _dictionary.disable_entry_hold_list(); _dictionary.clear(); _dictionary.getAllocator().freeze(); _dictionary.getAllocator().reclaim_all_memory(); diff --git a/searchlib/src/vespa/searchlib/tensor/CMakeLists.txt b/searchlib/src/vespa/searchlib/tensor/CMakeLists.txt index c8c5d4d4257..313863d8dcb 100644 --- a/searchlib/src/vespa/searchlib/tensor/CMakeLists.txt +++ b/searchlib/src/vespa/searchlib/tensor/CMakeLists.txt @@ -40,6 +40,7 @@ vespa_add_library(searchlib_tensor OBJECT tensor_buffer_store.cpp tensor_buffer_type_mapper.cpp tensor_deserialize.cpp + tensor_ext_attribute.cpp tensor_store.cpp DEPENDS ) diff --git a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp index c373f6bdcd0..c51d0ec7fd3 100644 --- a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp +++ b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp @@ -62,10 +62,10 @@ DenseTensorStore::BufferType::BufferType(const TensorSizeCalc &tensorSizeCalc, s DenseTensorStore::BufferType::~BufferType() = default; void -DenseTensorStore::BufferType::cleanHold(void *buffer, size_t offset, - ElemCount numElems, CleanContext) +DenseTensorStore::BufferType::clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext) { - memset(static_cast<char *>(buffer) + offset, 0, numElems); + auto num_elems = num_entries * getArraySize(); + memset(static_cast<char *>(buffer) + offset * getArraySize(), 0, num_elems); } const vespalib::alloc::MemoryAllocator* @@ -107,7 +107,7 @@ DenseTensorStore::allocRawBuffer() { size_t bufSize = getBufSize(); size_t alignedBufSize = _tensorSizeCalc.alignedSize(); - auto result = _concreteStore.freeListRawAllocator<char>(0u).alloc(alignedBufSize); + auto result = _concreteStore.freeListRawAllocator<char>(0u).alloc(1); clearPadAreaAfterBuffer(result.data, bufSize, alignedBufSize); return result; } @@ -118,7 +118,7 @@ DenseTensorStore::holdTensor(EntryRef ref) if (!ref.valid()) { return; } - _concreteStore.holdElem(ref, _tensorSizeCalc.alignedSize()); + _concreteStore.hold_entry(ref); } TensorStore::EntryRef diff --git a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.h b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.h index 9e326e0ab1e..0dd483e7f08 100644 --- a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.h +++ b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.h @@ -44,7 +44,7 @@ public: public: BufferType(const TensorSizeCalc &tensorSizeCalc, std::shared_ptr<vespalib::alloc::MemoryAllocator> allocator); ~BufferType() override; - void cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) override; + void clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) override; const vespalib::alloc::MemoryAllocator* get_memory_allocator() const override; }; private: diff --git a/searchlib/src/vespa/searchlib/tensor/direct_tensor_store.cpp b/searchlib/src/vespa/searchlib/tensor/direct_tensor_store.cpp index fa13ab6303c..8526138fd31 100644 --- a/searchlib/src/vespa/searchlib/tensor/direct_tensor_store.cpp +++ b/searchlib/src/vespa/searchlib/tensor/direct_tensor_store.cpp @@ -30,11 +30,11 @@ DirectTensorStore::TensorBufferType::TensorBufferType() } void -DirectTensorStore::TensorBufferType::cleanHold(void* buffer, size_t offset, ElemCount num_elems, CleanContext clean_ctx) +DirectTensorStore::TensorBufferType::clean_hold(void* buffer, size_t offset, EntryCount num_entries, CleanContext clean_ctx) { TensorSP* elem = static_cast<TensorSP*>(buffer) + offset; const auto& empty = empty_entry(); - for (size_t i = 0; i < num_elems; ++i) { + for (size_t i = 0; i < num_entries; ++i) { clean_ctx.extraBytesCleaned((*elem)->get_memory_usage().allocatedBytes()); *elem = empty; ++elem; @@ -69,7 +69,7 @@ DirectTensorStore::holdTensor(EntryRef ref) } const auto& tensor = _tensor_store.getEntry(ref); assert(tensor); - _tensor_store.holdElem(ref, 1, tensor->get_memory_usage().allocatedBytes()); + _tensor_store.hold_entry(ref, tensor->get_memory_usage().allocatedBytes()); } EntryRef diff --git a/searchlib/src/vespa/searchlib/tensor/direct_tensor_store.h b/searchlib/src/vespa/searchlib/tensor/direct_tensor_store.h index 01084e89776..1230494fe41 100644 --- a/searchlib/src/vespa/searchlib/tensor/direct_tensor_store.h +++ b/searchlib/src/vespa/searchlib/tensor/direct_tensor_store.h @@ -20,7 +20,7 @@ namespace search::tensor { */ class DirectTensorStore : public TensorStore { private: - // Note: Must use SP (instead of UP) because of fallbackCopy() and initializeReservedElements() in BufferType, + // Note: Must use SP (instead of UP) because of fallback_copy() and initialize_reserved_entries() in BufferType, // and implementation of move(). using TensorSP = std::shared_ptr<vespalib::eval::Value>; using TensorStoreType = vespalib::datastore::DataStore<TensorSP>; @@ -32,7 +32,7 @@ private: using CleanContext = typename ParentType::CleanContext; public: TensorBufferType(); - void cleanHold(void* buffer, size_t offset, ElemCount num_elems, CleanContext clean_ctx) override; + void clean_hold(void* buffer, size_t offset, EntryCount num_entries, CleanContext clean_ctx) override; }; TensorStoreType _tensor_store; diff --git a/searchlib/src/vespa/searchlib/tensor/large_subspaces_buffer_type.cpp b/searchlib/src/vespa/searchlib/tensor/large_subspaces_buffer_type.cpp index cb074348f08..5d3b2206703 100644 --- a/searchlib/src/vespa/searchlib/tensor/large_subspaces_buffer_type.cpp +++ b/searchlib/src/vespa/searchlib/tensor/large_subspaces_buffer_type.cpp @@ -12,7 +12,7 @@ using vespalib::alloc::MemoryAllocator; namespace search::tensor { LargeSubspacesBufferType::LargeSubspacesBufferType(const AllocSpec& spec, std::shared_ptr<MemoryAllocator> memory_allocator, TensorBufferTypeMapper& type_mapper) noexcept - : ParentType(1u, spec.minArraysInBuffer, spec.maxArraysInBuffer, spec.numArraysForNewBuffer, spec.allocGrowFactor), + : ParentType(1u, spec.min_entries_in_buffer, spec.max_entries_in_buffer, spec.num_entries_for_new_buffer, spec.allocGrowFactor), _memory_allocator(std::move(memory_allocator)), _ops(type_mapper.get_tensor_buffer_operations()) { @@ -21,10 +21,10 @@ LargeSubspacesBufferType::LargeSubspacesBufferType(const AllocSpec& spec, std::s LargeSubspacesBufferType::~LargeSubspacesBufferType() = default; void -LargeSubspacesBufferType::cleanHold(void* buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) +LargeSubspacesBufferType::clean_hold(void* buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) { auto elem = static_cast<ArrayType*>(buffer) + offset; - for (size_t i = 0; i < numElems; ++i) { + for (size_t i = 0; i < num_entries; ++i) { if (!elem->empty()) { cleanCtx.extraBytesCleaned(elem->size()); _ops.reclaim_labels({elem->data(), elem->size()}); @@ -35,10 +35,10 @@ LargeSubspacesBufferType::cleanHold(void* buffer, size_t offset, ElemCount numEl } void -LargeSubspacesBufferType::destroyElements(void *buffer, ElemCount numElems) +LargeSubspacesBufferType::destroy_entries(void *buffer, EntryCount num_entries) { auto elem = static_cast<ArrayType*>(buffer); - for (size_t i = 0; i < numElems; ++i) { + for (size_t i = 0; i < num_entries; ++i) { if (!elem->empty()) { _ops.reclaim_labels({elem->data(), elem->size()}); ArrayType().swap(*elem); @@ -48,11 +48,11 @@ LargeSubspacesBufferType::destroyElements(void *buffer, ElemCount numElems) } void -LargeSubspacesBufferType::fallbackCopy(void *newBuffer, const void *oldBuffer, ElemCount numElems) +LargeSubspacesBufferType::fallback_copy(void *newBuffer, const void *oldBuffer, EntryCount num_entries) { auto old_elems = static_cast<const ArrayType*>(oldBuffer); auto new_elems = static_cast<ArrayType*>(newBuffer); - for (size_t i = 0; i < numElems; ++i) { + for (size_t i = 0; i < num_entries; ++i) { auto& old_elem = old_elems[i]; new (new_elems + i) ArrayType(old_elem); if (!old_elem.empty()) { @@ -62,12 +62,12 @@ LargeSubspacesBufferType::fallbackCopy(void *newBuffer, const void *oldBuffer, E } void -LargeSubspacesBufferType::initializeReservedElements(void *buffer, ElemCount reservedElements) +LargeSubspacesBufferType::initialize_reserved_entries(void *buffer, EntryCount reserved_entries) { - auto new_elems = static_cast<ArrayType*>(buffer); + auto new_entries = static_cast<ArrayType*>(buffer); const auto& empty = empty_entry(); - for (size_t i = 0; i < reservedElements; ++i) { - new (new_elems + i) ArrayType(empty); + for (size_t i = 0; i < reserved_entries; ++i) { + new (new_entries + i) ArrayType(empty); } } diff --git a/searchlib/src/vespa/searchlib/tensor/large_subspaces_buffer_type.h b/searchlib/src/vespa/searchlib/tensor/large_subspaces_buffer_type.h index cfab8ef20af..8cce08e9d81 100644 --- a/searchlib/src/vespa/searchlib/tensor/large_subspaces_buffer_type.h +++ b/searchlib/src/vespa/searchlib/tensor/large_subspaces_buffer_type.h @@ -30,10 +30,10 @@ class LargeSubspacesBufferType : public vespalib::datastore::BufferType<vespalib public: LargeSubspacesBufferType(const AllocSpec& spec, std::shared_ptr<vespalib::alloc::MemoryAllocator> memory_allocator, TensorBufferTypeMapper& type_mapper) noexcept; ~LargeSubspacesBufferType() override; - void cleanHold(void* buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) override; - void destroyElements(void *buffer, ElemCount numElems) override; - void fallbackCopy(void *newBuffer, const void *oldBuffer, ElemCount numElems) override; - void initializeReservedElements(void *buffer, ElemCount reservedElements) override; + void clean_hold(void* buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) override; + void destroy_entries(void *buffer, EntryCount num_entries) override; + void fallback_copy(void *newBuffer, const void *oldBuffer, EntryCount num_entries) override; + void initialize_reserved_entries(void *buffer, EntryCount reserved_entries) override; const vespalib::alloc::MemoryAllocator* get_memory_allocator() const override; }; diff --git a/searchlib/src/vespa/searchlib/tensor/small_subspaces_buffer_type.cpp b/searchlib/src/vespa/searchlib/tensor/small_subspaces_buffer_type.cpp index 6a71388a3b9..7b54182f062 100644 --- a/searchlib/src/vespa/searchlib/tensor/small_subspaces_buffer_type.cpp +++ b/searchlib/src/vespa/searchlib/tensor/small_subspaces_buffer_type.cpp @@ -10,7 +10,7 @@ using vespalib::alloc::MemoryAllocator; namespace search::tensor { SmallSubspacesBufferType::SmallSubspacesBufferType(uint32_t array_size, const AllocSpec& spec, std::shared_ptr<MemoryAllocator> memory_allocator, TensorBufferTypeMapper& type_mapper) noexcept - : ParentType(array_size, spec.minArraysInBuffer, spec.maxArraysInBuffer, spec.numArraysForNewBuffer, spec.allocGrowFactor), + : ParentType(array_size, spec.min_entries_in_buffer, spec.max_entries_in_buffer, spec.num_entries_for_new_buffer, spec.allocGrowFactor), _memory_allocator(std::move(memory_allocator)), _ops(type_mapper.get_tensor_buffer_operations()) { @@ -19,45 +19,45 @@ SmallSubspacesBufferType::SmallSubspacesBufferType(uint32_t array_size, const Al SmallSubspacesBufferType::~SmallSubspacesBufferType() = default; void -SmallSubspacesBufferType::cleanHold(void* buffer, size_t offset, ElemCount numElems, CleanContext) +SmallSubspacesBufferType::clean_hold(void* buffer, size_t offset, EntryCount num_entries, CleanContext) { - char* elem = static_cast<char *>(buffer) + offset; - while (numElems >= getArraySize()) { + char* elem = static_cast<char *>(buffer) + offset * getArraySize(); + while (num_entries >= 1) { _ops.reclaim_labels(vespalib::ArrayRef<char>(elem, getArraySize())); elem += getArraySize(); - numElems -= getArraySize(); + --num_entries; } } void -SmallSubspacesBufferType::destroyElements(void *buffer, ElemCount numElems) +SmallSubspacesBufferType::destroy_entries(void *buffer, EntryCount num_entries) { char* elem = static_cast<char *>(buffer); - while (numElems >= getArraySize()) { + while (num_entries >= 1) { _ops.reclaim_labels(vespalib::ArrayRef<char>(elem, getArraySize())); elem += getArraySize(); - numElems -= getArraySize(); + --num_entries; } } void -SmallSubspacesBufferType::fallbackCopy(void *newBuffer, const void *oldBuffer, ElemCount numElems) +SmallSubspacesBufferType::fallback_copy(void *newBuffer, const void *oldBuffer, EntryCount num_entries) { - if (numElems > 0) { - memcpy(newBuffer, oldBuffer, numElems); + if (num_entries > 0) { + memcpy(newBuffer, oldBuffer, num_entries * getArraySize()); } const char *elem = static_cast<const char *>(oldBuffer); - while (numElems >= getArraySize()) { + while (num_entries >= 1) { _ops.copied_labels(unconstify(vespalib::ConstArrayRef<char>(elem, getArraySize()))); elem += getArraySize(); - numElems -= getArraySize(); + --num_entries; } } void -SmallSubspacesBufferType::initializeReservedElements(void *buffer, ElemCount reservedElements) +SmallSubspacesBufferType::initialize_reserved_entries(void *buffer, EntryCount reserved_entries) { - memset(buffer, 0, reservedElements); + memset(buffer, 0, reserved_entries * getArraySize()); } const vespalib::alloc::MemoryAllocator* diff --git a/searchlib/src/vespa/searchlib/tensor/small_subspaces_buffer_type.h b/searchlib/src/vespa/searchlib/tensor/small_subspaces_buffer_type.h index 5622e9970b8..2f287ef1f3d 100644 --- a/searchlib/src/vespa/searchlib/tensor/small_subspaces_buffer_type.h +++ b/searchlib/src/vespa/searchlib/tensor/small_subspaces_buffer_type.h @@ -30,10 +30,10 @@ public: SmallSubspacesBufferType& operator=(SmallSubspacesBufferType&&) noexcept = delete; SmallSubspacesBufferType(uint32_t array_size, const AllocSpec& spec, std::shared_ptr<vespalib::alloc::MemoryAllocator> memory_allocator, TensorBufferTypeMapper& type_mapper) noexcept; ~SmallSubspacesBufferType() override; - void cleanHold(void* buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) override; - void destroyElements(void *buffer, ElemCount numElems) override; - void fallbackCopy(void *newBuffer, const void *oldBuffer, ElemCount numElems) override; - void initializeReservedElements(void *buffer, ElemCount reservedElements) override; + void clean_hold(void* buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) override; + void destroy_entries(void *buffer, EntryCount num_entries) override; + void fallback_copy(void *newBuffer, const void *oldBuffer, EntryCount num_entries) override; + void initialize_reserved_entries(void *buffer, EntryCount reserved_entries) override; const vespalib::alloc::MemoryAllocator* get_memory_allocator() const override; }; diff --git a/searchlib/src/vespa/searchlib/tensor/tensor_ext_attribute.cpp b/searchlib/src/vespa/searchlib/tensor/tensor_ext_attribute.cpp new file mode 100644 index 00000000000..19c8cf6053b --- /dev/null +++ b/searchlib/src/vespa/searchlib/tensor/tensor_ext_attribute.cpp @@ -0,0 +1,181 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "tensor_ext_attribute.h" +#include "serialized_tensor_ref.h" +#include "vector_bundle.h" +#include <vespa/eval/eval/fast_value.h> +#include <vespa/eval/eval/tensor_spec.h> +#include <vespa/eval/eval/value.h> +#include <vespa/eval/eval/value_codec.h> +#include <vespa/searchcommon/attribute/config.h> + +#include <vespa/log/log.h> +LOG_SETUP(".searchlib.tensor.tensor_ext_attribute"); + +using vespalib::eval::FastValueBuilderFactory; +using vespalib::eval::TensorSpec; +using vespalib::eval::TypedCells; +using vespalib::eval::Value; +using vespalib::eval::ValueType; + +namespace search::tensor { + +namespace { + +std::unique_ptr<Value> +create_empty_tensor(const ValueType& type) +{ + const auto &factory = FastValueBuilderFactory::get(); + TensorSpec empty_spec(type.to_spec()); + return vespalib::eval::value_from_spec(empty_spec, factory); +} + +} + +TensorExtAttribute::TensorExtAttribute(const vespalib::string& name, const Config& cfg) + : NotImplementedAttribute(name, cfg), + ITensorAttribute(), + IExtendAttribute(), + _subspace_type(cfg.tensorType()), + _empty(_subspace_type), + _empty_tensor(create_empty_tensor(cfg.tensorType())) +{ +} + +TensorExtAttribute::~TensorExtAttribute() = default; + +const ITensorAttribute* +TensorExtAttribute::asTensorAttribute() const +{ + return this; +} + +void +TensorExtAttribute::onCommit() +{ + LOG_ABORT("should not be reached"); +} + +void +TensorExtAttribute::onUpdateStat() +{ +} + +bool +TensorExtAttribute::addDoc(DocId& docId) +{ + docId = _data.size(); + _data.emplace_back(nullptr); + incNumDocs(); + setCommittedDocIdLimit(getNumDocs()); + return true; +} + +bool +TensorExtAttribute::add(const vespalib::eval::Value& v, int32_t) +{ + _data.back() = &v; + return true; +} + +IExtendAttribute* +TensorExtAttribute::getExtendInterface() +{ + return this; +} + +TypedCells +TensorExtAttribute::get_vector(uint32_t docid, uint32_t subspace) const +{ + auto vectors = get_vectors(docid); + return (subspace < vectors.subspaces()) ? vectors.cells(subspace) : _empty.cells(); +} + +VectorBundle +TensorExtAttribute::get_vectors(uint32_t docid) const +{ + auto tensor = _data[docid]; + if (tensor == nullptr) { + return VectorBundle(); + } + return VectorBundle(tensor->cells().data, tensor->index().size(), _subspace_type); +} + +std::unique_ptr<Value> +TensorExtAttribute::getTensor(uint32_t docid) const +{ + auto tensor = _data[docid]; + if (tensor == nullptr) { + return {}; + } + return FastValueBuilderFactory::get().copy(*tensor); +} + +std::unique_ptr<Value> +TensorExtAttribute::getEmptyTensor() const +{ + return FastValueBuilderFactory::get().copy(*_empty_tensor); +} + +TypedCells +TensorExtAttribute::extract_cells_ref(uint32_t docid) const +{ + return get_vector(docid, 0); +} + +const vespalib::eval::Value& +TensorExtAttribute::get_tensor_ref(uint32_t docid) const +{ + auto tensor = _data[docid]; + return (tensor == nullptr) ? *_empty_tensor : *tensor; +} + +SerializedTensorRef +TensorExtAttribute::get_serialized_tensor_ref(uint32_t) const +{ + notImplemented(); +} + +bool +TensorExtAttribute::supports_extract_cells_ref() const +{ + return getConfig().tensorType().is_dense(); +} + +bool +TensorExtAttribute::supports_get_tensor_ref() const +{ + return true; +} + +bool +TensorExtAttribute::supports_get_serialized_tensor_ref() const +{ + return false; +} + +const ValueType& +TensorExtAttribute::getTensorType() const +{ + return getConfig().tensorType(); +} + +TensorExtAttribute::DistanceMetric +TensorExtAttribute::distance_metric() const +{ + return getConfig().distance_metric(); +} + +uint32_t +TensorExtAttribute::get_num_docs() const +{ + return _data.size(); +} + +void +TensorExtAttribute::get_state(const vespalib::slime::Inserter& inserter) const +{ + (void) inserter; +} + +} diff --git a/searchlib/src/vespa/searchlib/tensor/tensor_ext_attribute.h b/searchlib/src/vespa/searchlib/tensor/tensor_ext_attribute.h new file mode 100644 index 00000000000..a58426cd146 --- /dev/null +++ b/searchlib/src/vespa/searchlib/tensor/tensor_ext_attribute.h @@ -0,0 +1,54 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include "i_tensor_attribute.h" +#include "empty_subspace.h" +#include "subspace_type.h" +#include <vespa/searchlib/attribute/not_implemented_attribute.h> +#include <vespa/vespalib/stllike/allocator.h> + +namespace search::tensor { + +/** + * Attribute vector storing a pointer to single tensor value per + * document in streaming search. The tensor is not owned by this + * attribute vector. + */ +class TensorExtAttribute : public NotImplementedAttribute, + public ITensorAttribute, + public IExtendAttribute +{ + std::vector<const vespalib::eval::Value*> _data; + SubspaceType _subspace_type; + EmptySubspace _empty; + std::unique_ptr<vespalib::eval::Value> _empty_tensor; +public: + TensorExtAttribute(const vespalib::string& name, const Config& cfg); + ~TensorExtAttribute() override; + const ITensorAttribute* asTensorAttribute() const override; + void onCommit() override; + void onUpdateStat() override; + bool addDoc(DocId& docId) override; + bool add(const vespalib::eval::Value& v, int32_t) override; + IExtendAttribute* getExtendInterface() override; + // DocVectorAccess API + vespalib::eval::TypedCells get_vector(uint32_t docid, uint32_t subspace) const override; + VectorBundle get_vectors(uint32_t docid) const override; + + // ITensorAttribute API + std::unique_ptr<vespalib::eval::Value> getTensor(uint32_t docid) const override; + std::unique_ptr<vespalib::eval::Value> getEmptyTensor() const override; + vespalib::eval::TypedCells extract_cells_ref(uint32_t docid) const override; + const vespalib::eval::Value& get_tensor_ref(uint32_t docid) const override; + SerializedTensorRef get_serialized_tensor_ref(uint32_t docid) const override; + bool supports_extract_cells_ref() const override; + bool supports_get_tensor_ref() const override; + bool supports_get_serialized_tensor_ref() const override; + const vespalib::eval::ValueType & getTensorType() const override; + search::attribute::DistanceMetric distance_metric() const override; + uint32_t get_num_docs() const override; + void get_state(const vespalib::slime::Inserter& inserter) const override; +}; + +} diff --git a/storage/src/vespa/storage/bucketdb/btree_lockable_map.hpp b/storage/src/vespa/storage/bucketdb/btree_lockable_map.hpp index 1c0f9ec7a59..e24e97ff2a4 100644 --- a/storage/src/vespa/storage/bucketdb/btree_lockable_map.hpp +++ b/storage/src/vespa/storage/bucketdb/btree_lockable_map.hpp @@ -49,7 +49,7 @@ struct BTreeLockableMap<T>::ValueTraits { return store.addEntry(value).ref(); } static void remove_by_wrapped_value(DataStoreType& store, uint64_t value) noexcept { - store.holdElem(entry_ref_from_value(value), 1); + store.hold_entry(entry_ref_from_value(value)); } static ValueType unwrap_from_key_value(const DataStoreType& store, [[maybe_unused]] uint64_t key, uint64_t value) { return store.getEntry(entry_ref_from_value(value)); diff --git a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp index 92b9e832a23..1d29a8795d5 100644 --- a/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp +++ b/storage/src/vespa/storage/persistence/filestorage/filestorhandlerimpl.cpp @@ -1054,7 +1054,7 @@ FileStorHandlerImpl::Stripe::waitUntilNoLocks() const { std::unique_lock guard(*_lock); while (!_lockedBuckets.empty()) { - _cond->wait(guard); + _cond->wait_for(guard, 100ms); } } @@ -1062,7 +1062,7 @@ void FileStorHandlerImpl::Stripe::waitInactive(const AbortBucketOperationsCommand& cmd) const { std::unique_lock guard(*_lock); while (hasActive(guard, cmd)) { - _cond->wait(guard); + _cond->wait_for(guard, 100ms); } } diff --git a/streamingvisitors/src/vespa/searchvisitor/searchvisitor.cpp b/streamingvisitors/src/vespa/searchvisitor/searchvisitor.cpp index d0e3d1f038b..2caf2de1d0b 100644 --- a/streamingvisitors/src/vespa/searchvisitor/searchvisitor.cpp +++ b/streamingvisitors/src/vespa/searchvisitor/searchvisitor.cpp @@ -7,6 +7,7 @@ #include <vespa/persistence/spi/docentry.h> #include <vespa/document/datatype/positiondatatype.h> #include <vespa/document/datatype/documenttype.h> +#include <vespa/document/datatype/tensor_data_type.h> #include <vespa/document/datatype/weightedsetdatatype.h> #include <vespa/document/datatype/mapdatatype.h> #include <vespa/searchlib/aggregation/modifiers.h> @@ -14,6 +15,7 @@ #include <vespa/searchlib/common/packets.h> #include <vespa/searchlib/uca/ucaconverter.h> #include <vespa/searchlib/features/setup.h> +#include <vespa/searchlib/tensor/tensor_ext_attribute.h> #include <vespa/searchcommon/attribute/config.h> #include <vespa/vespalib/geo/zcurve.h> #include <vespa/vespalib/objects/nbostream.h> @@ -99,6 +101,16 @@ createMultiValueAttribute(const vespalib::string & name, const document::FieldVa return {}; } +const document::TensorDataType* +get_tensor_type(const document::FieldValue& fv) +{ + auto tfv = dynamic_cast<const document::TensorFieldValue*>(&fv); + if (tfv == nullptr) { + return nullptr; + } + return dynamic_cast<const document::TensorDataType*>(tfv->getDataType()); +} + AttributeVector::SP createAttribute(const vespalib::string & name, const document::FieldValue & fv) { @@ -111,6 +123,12 @@ createAttribute(const vespalib::string & name, const document::FieldValue & fv) return std::make_shared<search::SingleStringExtAttribute>(name); } else if (fv.isA(document::FieldValue::Type::RAW)) { return std::make_shared<search::attribute::SingleRawExtAttribute>(name); + } else if (fv.isA(document::FieldValue::Type::TENSOR) && get_tensor_type(fv) != nullptr) { + search::attribute::Config cfg(search::attribute::BasicType::TENSOR, search::attribute::CollectionType::SINGLE); + auto tdt = get_tensor_type(fv); + assert(tdt != nullptr); + cfg.setTensorType(tdt->getTensorType()); + return std::make_shared<search::tensor::TensorExtAttribute>(name, cfg); } else { LOG(debug, "Can not make an attribute out of %s of type '%s'.", name.c_str(), fv.className()); } @@ -444,6 +462,14 @@ SearchVisitor::AttributeInserter::onPrimitive(uint32_t, const Content & c) } else if (_attribute.is_raw_type()) { auto raw_value = value.getAsRaw(); attr.add(vespalib::ConstArrayRef<char>(raw_value.first, raw_value.second), c.getWeight()); + } else if (_attribute.isTensorType()) { + auto tfvalue = dynamic_cast<const document::TensorFieldValue*>(&value); + if (tfvalue != nullptr) { + auto tensor = tfvalue->getAsTensorPtr(); + if (tensor != nullptr) { + attr.add(*tensor, c.getWeight()); + } + } } else { assert(false && "We got an attribute vector that is of an unknown type"); } diff --git a/vdslib/src/tests/distribution/distributiontest.cpp b/vdslib/src/tests/distribution/distributiontest.cpp index a10aca45b33..b5c756aece9 100644 --- a/vdslib/src/tests/distribution/distributiontest.cpp +++ b/vdslib/src/tests/distribution/distributiontest.cpp @@ -17,8 +17,9 @@ #include <vespa/vespalib/util/size_literals.h> #include <gmock/gmock.h> #include <chrono> -#include <thread> #include <fstream> +#include <iterator> +#include <thread> using namespace ::testing; @@ -434,7 +435,8 @@ TEST(DistributionTest, test_distribution) _distribution[i].second = distr.getIdealStorageNodes( systemState, document::BucketId(26, i)); sort(_distribution[i].second.begin(), _distribution[i].second.end()); - unique(_distribution[i].second.begin(), _distribution[i].second.end()); + auto unique_nodes = std::distance(_distribution[i].second.begin(), unique(_distribution[i].second.begin(), _distribution[i].second.end())); + _distribution[i].second.resize(unique_nodes); for (unsigned j = 0; j < _distribution[i].second.size(); j++) { _nodeCount[_distribution[i].second[j]]++; diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java index fe1e2b46830..2a688ad078b 100644 --- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java +++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java @@ -1226,7 +1226,7 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler { getProperty(request, TRACELEVEL, integerParser).ifPresent(parameters::setTraceLevel); - getProperty(request, CONTINUATION).map(ProgressToken::fromSerializedString).ifPresent(parameters::setResumeToken); + getProperty(request, CONTINUATION, ProgressToken::fromSerializedString).ifPresent(parameters::setResumeToken); parameters.setPriority(DocumentProtocol.Priority.NORMAL_4); getProperty(request, FROM_TIMESTAMP, unsignedLongParser).ifPresent(parameters::setFromTimestamp); diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java b/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java index 33e46ebc75f..31f4038c16e 100644 --- a/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java +++ b/vespajlib/src/main/java/com/yahoo/concurrent/maintenance/Maintainer.java @@ -37,15 +37,17 @@ public abstract class Maintainer implements Runnable { private final AtomicBoolean shutDown = new AtomicBoolean(); private final boolean ignoreCollision; private final Clock clock; + private final Double successFactorBaseline; public Maintainer(String name, Duration interval, Clock clock, JobControl jobControl, - JobMetrics jobMetrics, List<String> clusterHostnames, boolean ignoreCollision) { + JobMetrics jobMetrics, List<String> clusterHostnames, boolean ignoreCollision, Double successFactorBaseline) { this.name = name; this.interval = requireInterval(interval); this.jobControl = Objects.requireNonNull(jobControl); this.jobMetrics = Objects.requireNonNull(jobMetrics); this.ignoreCollision = ignoreCollision; this.clock = clock; + this.successFactorBaseline = successFactorBaseline; var startedAt = clock.instant(); Objects.requireNonNull(clusterHostnames); Duration initialDelay = staggeredDelay(interval, startedAt, HostName.getLocalhost(), clusterHostnames) @@ -55,6 +57,10 @@ public abstract class Maintainer implements Runnable { jobControl.started(name(), this); } + public Maintainer(String name, Duration interval, Clock clock, JobControl jobControl, + JobMetrics jobMetrics, List<String> clusterHostnames, boolean ignoreCollision) { + this(name, interval, clock, jobControl, jobMetrics, clusterHostnames, ignoreCollision, 1.0); + } @Override public void run() { lockAndMaintain(false); @@ -91,16 +97,16 @@ public abstract class Maintainer implements Runnable { /** * Called once each time this maintenance job should run. * - * @return the degree to which the run was successful - a number between 0 (no success), to 1 (complete success). + * @return the degree to which the run was deviated from the successFactorBaseline - a number between -1 (no success), to 0 (complete success). * Note that this indicates whether something is wrong, so e.g if the call did nothing because it should do - * nothing, 1.0 should be returned. + * nothing, 0.0 should be returned. */ protected abstract double maintain(); - /** Convenience methods to convert attempts and failures into a success factor */ - protected final double asSuccessFactor(int attempts, int failures) { + /** Convenience methods to convert attempts and failures into a success factor, and return */ + protected final double asSuccessFactorDeviation(int attempts, int failures) { double factor = attempts == 0 ? 1.0 : 1 - (double)failures / attempts; - return new BigDecimal(factor).setScale(2, RoundingMode.HALF_UP).doubleValue(); + return new BigDecimal(factor - successFactorBaseline).setScale(2, RoundingMode.HALF_UP).doubleValue(); } /** Returns the interval at which this job is set to run */ @@ -111,14 +117,14 @@ public abstract class Maintainer implements Runnable { if (!force && !jobControl.isActive(name())) return; log.log(Level.FINE, () -> "Running " + this.getClass().getSimpleName()); - double successFactor = 0; + double successFactorDeviation = 0; long startTime = clock.millis(); try (var lock = jobControl.lockJob(name())) { - successFactor = maintain(); + successFactorDeviation = maintain(); } catch (UncheckedTimeoutException e) { if (ignoreCollision) - successFactor = 1; + successFactorDeviation = 0; else log.log(Level.WARNING, this + " collided with another run. Will retry in " + interval); } @@ -127,7 +133,7 @@ public abstract class Maintainer implements Runnable { } finally { long endTime = clock.millis(); - jobMetrics.completed(name(), successFactor, endTime - startTime); + jobMetrics.completed(name(), successFactorDeviation, endTime - startTime); } log.log(Level.FINE, () -> "Finished " + this.getClass().getSimpleName()); } diff --git a/vespajlib/src/main/java/com/yahoo/slime/SlimeUtils.java b/vespajlib/src/main/java/com/yahoo/slime/SlimeUtils.java index f9230ab6df6..6acd0679da2 100644 --- a/vespajlib/src/main/java/com/yahoo/slime/SlimeUtils.java +++ b/vespajlib/src/main/java/com/yahoo/slime/SlimeUtils.java @@ -166,81 +166,32 @@ public class SlimeUtils { false); } - private static class Equal { - protected final Inspector rhsInspector; - - protected boolean equal = true; - - public Equal(Inspector rhsInspector) { this.rhsInspector = rhsInspector; } - - public boolean isEqual() { return equal; } - } - - private static class EqualArray extends Equal implements ArrayTraverser { - public EqualArray(Inspector rhsInspector) { super(rhsInspector); } - - @Override - public void entry(int idx, Inspector inspector) { - if (equal) { - equal = inspector.equalTo(rhsInspector.entry(idx)); - } - } - } - - private static class EqualObject extends Equal implements ObjectTraverser { - public EqualObject(Inspector rhsInspector) { super(rhsInspector); } - - @Override - public void field(String name, Inspector inspector) { - if (equal) { - equal = inspector.equalTo(rhsInspector.field(name)); - } - } - } - public static boolean equalTo(Inspector a, Inspector b) { - boolean equal = a.type() == b.type(); - - if (equal) { - switch (a.type()) { - case NIX: - equal = a.valid() == b.valid(); - break; - case BOOL: - equal = a.asBool() == b.asBool(); - break; - case LONG: - equal = a.asLong() == b.asLong(); - break; - case DOUBLE: - equal = Double.compare(a.asDouble(), b.asDouble()) == 0; - break; - case STRING: - equal = a.asString().equals(b.asString()); - break; - case DATA: - equal = Arrays.equals(a.asData(), b.asData()); - break; - case ARRAY: - { - var traverser = new EqualArray(b); - a.traverse(traverser); - equal = traverser.isEqual() && (a.entries() == b.entries()); - } - break; - case OBJECT: - { - var traverser = new EqualObject(b); - a.traverse(traverser); - equal = traverser.isEqual() && (a.fields() == b.fields()); + if (a.type() != b.type()) return false; + + switch (a.type()) { + case NIX: return a.valid() == b.valid(); + case BOOL: return a.asBool() == b.asBool(); + case LONG: return a.asLong() == b.asLong(); + case DOUBLE: return Double.compare(a.asDouble(), b.asDouble()) == 0; + case STRING: return a.asString().equals(b.asString()); + case DATA: return Arrays.equals(a.asData(), b.asData()); + case ARRAY: { + if (a.entries() != b.entries()) return false; + for (int i = 0; i < a.entries(); i++) { + if (!equalTo(a.entry(i), b.entry(i))) return false; } - break; - default: - assert(false); - break; + return true; } + case OBJECT: { + if (a.fields() != b.fields()) return false; + boolean[] equal = new boolean[]{ true }; + a.traverse((String key, Inspector value) -> { + if (equal[0] && !equalTo(value, b.field(key))) equal[0] = false; + }); + return equal[0]; + } + default: throw new IllegalStateException("Unexpected type: " + a.type()); } - - return equal; } } diff --git a/vespalib/src/tests/btree/btree-stress/btree_stress_test.cpp b/vespalib/src/tests/btree/btree-stress/btree_stress_test.cpp index 44decb9bf91..31113f2b4f2 100644 --- a/vespalib/src/tests/btree/btree-stress/btree_stress_test.cpp +++ b/vespalib/src/tests/btree/btree-stress/btree_stress_test.cpp @@ -53,7 +53,7 @@ public: ~RealIntStore(); EntryRef add(uint32_t value) { return _store.addEntry(value); } AtomicEntryRef add_relaxed(uint32_t value) { return AtomicEntryRef(add(value)); } - void hold(const AtomicEntryRef& ref) { _store.holdElem(ref.load_relaxed(), 1); } + void hold(const AtomicEntryRef& ref) { _store.hold_entry(ref.load_relaxed()); } EntryRef move(EntryRef ref); void assign_generation(generation_t current_gen) { _store.assign_generation(current_gen); } void reclaim_memory(generation_t gen) { _store.reclaim_memory(gen); } diff --git a/vespalib/src/tests/btree/btree_test.cpp b/vespalib/src/tests/btree/btree_test.cpp index b8da9ea6042..afad3523fa3 100644 --- a/vespalib/src/tests/btree/btree_test.cpp +++ b/vespalib/src/tests/btree/btree_test.cpp @@ -1065,7 +1065,7 @@ adjustAllocatedBytes(size_t nodeCount, size_t nodeSize) TEST_F(BTreeTest, require_that_memory_usage_is_calculated) { constexpr size_t BASE_ALLOCATED = 28744u; - constexpr size_t BASE_USED = 24984; + constexpr size_t BASE_USED = 24936; typedef BTreeNodeAllocator<int32_t, int8_t, btree::NoAggregated, MyTraits::INTERNAL_SLOTS, MyTraits::LEAF_SLOTS> NodeAllocator; diff --git a/vespalib/src/tests/datastore/array_store/array_store_test.cpp b/vespalib/src/tests/datastore/array_store/array_store_test.cpp index ccc3ab88c31..1df03f6eb0a 100644 --- a/vespalib/src/tests/datastore/array_store/array_store_test.cpp +++ b/vespalib/src/tests/datastore/array_store/array_store_test.cpp @@ -29,16 +29,16 @@ constexpr float ALLOC_GROW_FACTOR = 0.2; } -template <typename TestT, typename EntryT, typename RefT = EntryRefT<19> > +template <typename TestT, typename ElemT, typename RefT = EntryRefT<19> > struct ArrayStoreTest : public TestT { using EntryRefType = RefT; - using ArrayStoreType = ArrayStore<EntryT, RefT>; + using ArrayStoreType = ArrayStore<ElemT, RefT>; using LargeArray = typename ArrayStoreType::LargeArray; using ConstArrayRef = typename ArrayStoreType::ConstArrayRef; - using EntryVector = std::vector<EntryT>; - using value_type = EntryT; - using ReferenceStore = vespalib::hash_map<EntryRef, EntryVector>; + using ElemVector = std::vector<ElemT>; + using value_type = ElemT; + using ReferenceStore = vespalib::hash_map<EntryRef, ElemVector>; AllocStats stats; ArrayStoreType store; @@ -61,11 +61,11 @@ struct ArrayStoreTest : public TestT add_using_allocate(false) {} ~ArrayStoreTest() override; - void assertAdd(const EntryVector &input) { + void assertAdd(const ElemVector &input) { EntryRef ref = add(input); assertGet(ref, input); } - EntryRef add(const EntryVector &input) { + EntryRef add(const ElemVector &input) { EntryRef result; if (add_using_allocate) { result = store.allocate(input.size()); @@ -82,16 +82,16 @@ struct ArrayStoreTest : public TestT refStore.insert(std::make_pair(result, input)); return result; } - void assertGet(EntryRef ref, const EntryVector &exp) const { + void assertGet(EntryRef ref, const ElemVector &exp) const { ConstArrayRef act = store.get(ref); - EXPECT_EQ(exp, EntryVector(act.begin(), act.end())); + EXPECT_EQ(exp, ElemVector(act.begin(), act.end())); } void remove(EntryRef ref) { ASSERT_EQ(1u, refStore.count(ref)); store.remove(ref); refStore.erase(ref); } - void remove(const EntryVector &input) { + void remove(const ElemVector &input) { remove(getEntryRef(input)); } uint32_t getBufferId(EntryRef ref) const { @@ -99,14 +99,14 @@ struct ArrayStoreTest : public TestT } void assertBufferState(EntryRef ref, const MemStats& expStats) { EXPECT_EQ(expStats._used, store.bufferState(ref).size()); - EXPECT_EQ(expStats._hold, store.bufferState(ref).stats().hold_elems()); - EXPECT_EQ(expStats._dead, store.bufferState(ref).stats().dead_elems()); + EXPECT_EQ(expStats._hold, store.bufferState(ref).stats().hold_entries()); + EXPECT_EQ(expStats._dead, store.bufferState(ref).stats().dead_entries()); } void assert_buffer_stats(EntryRef ref, const TestBufferStats& exp_stats) { const auto& state = store.bufferState(ref); EXPECT_EQ(exp_stats._used, state.size()); - EXPECT_EQ(exp_stats._hold, state.stats().hold_elems()); - EXPECT_EQ(exp_stats._dead, state.stats().dead_elems()); + EXPECT_EQ(exp_stats._hold, state.stats().hold_entries()); + EXPECT_EQ(exp_stats._dead, state.stats().dead_entries()); EXPECT_EQ(exp_stats._extra_used, state.stats().extra_used_bytes()); EXPECT_EQ(exp_stats._extra_hold, state.stats().extra_hold_bytes()); } @@ -121,7 +121,7 @@ struct ArrayStoreTest : public TestT assertGet(elem.first, elem.second); } } - void assert_ref_reused(const EntryVector& first, const EntryVector& second, bool should_reuse) { + void assert_ref_reused(const ElemVector& first, const ElemVector& second, bool should_reuse) { EntryRef ref1 = add(first); remove(ref1); reclaim_memory(); @@ -129,7 +129,7 @@ struct ArrayStoreTest : public TestT EXPECT_EQ(should_reuse, (ref2 == ref1)); assertGet(ref2, second); } - EntryRef getEntryRef(const EntryVector &input) { + EntryRef getEntryRef(const ElemVector &input) { for (auto itr = refStore.begin(); itr != refStore.end(); ++itr) { if (itr->second == input) { return itr->first; @@ -160,12 +160,12 @@ struct ArrayStoreTest : public TestT } refStore = compactedRefStore; } - size_t entrySize() const { return sizeof(EntryT); } + size_t elem_size() const { return sizeof(ElemT); } size_t largeArraySize() const { return sizeof(LargeArray); } }; -template <typename TestT, typename EntryT, typename RefT> -ArrayStoreTest<TestT, EntryT, RefT>::~ArrayStoreTest() = default; +template <typename TestT, typename ElemT, typename RefT> +ArrayStoreTest<TestT, ElemT, RefT>::~ArrayStoreTest() = default; struct TestParam { bool add_using_allocate; @@ -214,8 +214,8 @@ TEST_P(NumberStoreTest, control_static_sizes) { EXPECT_EQ(240u + sizeof_deque, sizeof(NumberStoreTest::ArrayStoreType::DataStoreType)); EXPECT_EQ(104u, sizeof(NumberStoreTest::ArrayStoreType::SmallBufferType)); MemoryUsage usage = store.getMemoryUsage(); - EXPECT_EQ(202120u, usage.allocatedBytes()); - EXPECT_EQ(197752u, usage.usedBytes()); + EXPECT_EQ(202116u, usage.allocatedBytes()); + EXPECT_EQ(197656u, usage.usedBytes()); } TEST_P(NumberStoreTest, add_and_get_small_arrays_of_trivial_type) @@ -246,15 +246,15 @@ TEST_F(StringStoreTest, add_and_get_large_arrays_of_non_trivial_type) assertAdd({"ddd", "eee", "ffff", "gggg", "hhhh"}); } -TEST_P(NumberStoreTest, elements_are_put_on_hold_when_a_small_array_is_removed) +TEST_P(NumberStoreTest, entries_are_put_on_hold_when_a_small_array_is_removed) { EntryRef ref = add({1,2,3}); - assertBufferState(ref, MemStats().used(3).hold(0)); + assertBufferState(ref, MemStats().used(1).hold(0)); store.remove(ref); - assertBufferState(ref, MemStats().used(3).hold(3)); + assertBufferState(ref, MemStats().used(1).hold(1)); } -TEST_P(NumberStoreTest, elements_are_put_on_hold_when_a_large_array_is_removed) +TEST_P(NumberStoreTest, entries_are_put_on_hold_when_a_large_array_is_removed) { EntryRef ref = add({1,2,3,4}); // Note: The first buffer has the first element reserved -> we expect 2 elements used here. @@ -319,7 +319,7 @@ test_compaction(NumberStoreBasicTest &f) f.remove(f.add({5,5})); f.reclaim_memory(); f.assertBufferState(size1Ref, MemStats().used(1).dead(0)); - f.assertBufferState(size2Ref, MemStats().used(4).dead(2)); + f.assertBufferState(size2Ref, MemStats().used(2).dead(1)); f.assertBufferState(size3Ref, MemStats().used(2).dead(1)); // Note: First element is reserved uint32_t size1BufferId = f.getBufferId(size1Ref); uint32_t size2BufferId = f.getBufferId(size2Ref); @@ -363,8 +363,8 @@ void testCompaction(NumberStoreTest &f, bool compactMemory, bool compactAddressS f.remove(f.add({7})); f.reclaim_memory(); f.assertBufferState(size1Ref, MemStats().used(3).dead(2)); - f.assertBufferState(size2Ref, MemStats().used(2).dead(0)); - f.assertBufferState(size3Ref, MemStats().used(6).dead(3)); + f.assertBufferState(size2Ref, MemStats().used(1).dead(0)); + f.assertBufferState(size3Ref, MemStats().used(2).dead(1)); uint32_t size1BufferId = f.getBufferId(size1Ref); uint32_t size2BufferId = f.getBufferId(size2Ref); uint32_t size3BufferId = f.getBufferId(size3Ref); @@ -434,22 +434,22 @@ TEST_P(NumberStoreTest, used_onHold_and_dead_memory_usage_is_tracked_for_small_a { MemStats exp(store.getMemoryUsage()); add({1,2,3}); - assertMemoryUsage(exp.used(entrySize() * 3)); + assertMemoryUsage(exp.used(elem_size() * 3)); remove({1,2,3}); - assertMemoryUsage(exp.hold(entrySize() * 3)); + assertMemoryUsage(exp.hold(elem_size() * 3)); reclaim_memory(); - assertMemoryUsage(exp.holdToDead(entrySize() * 3)); + assertMemoryUsage(exp.holdToDead(elem_size() * 3)); } TEST_P(NumberStoreTest, used_onHold_and_dead_memory_usage_is_tracked_for_large_arrays) { MemStats exp(store.getMemoryUsage()); add({1,2,3,4}); - assertMemoryUsage(exp.used(largeArraySize() + entrySize() * 4)); + assertMemoryUsage(exp.used(largeArraySize() + elem_size() * 4)); remove({1,2,3,4}); - assertMemoryUsage(exp.hold(largeArraySize() + entrySize() * 4)); + assertMemoryUsage(exp.hold(largeArraySize() + elem_size() * 4)); reclaim_memory(); - assertMemoryUsage(exp.decUsed(entrySize() * 4).decHold(largeArraySize() + entrySize() * 4). + assertMemoryUsage(exp.decUsed(elem_size() * 4).decHold(largeArraySize() + elem_size() * 4). dead(largeArraySize())); } diff --git a/vespalib/src/tests/datastore/array_store_config/array_store_config_test.cpp b/vespalib/src/tests/datastore/array_store_config/array_store_config_test.cpp index fcf1acd5cd3..171e7216638 100644 --- a/vespalib/src/tests/datastore/array_store_config/array_store_config_test.cpp +++ b/vespalib/src/tests/datastore/array_store_config/array_store_config_test.cpp @@ -22,32 +22,32 @@ struct Fixture Fixture(uint32_t maxSmallArrayTypeId, size_t hugePageSize, size_t smallPageSize, - size_t minNumArraysForNewBuffer) + size_t min_num_entries_for_new_buffer) : cfg(ArrayStoreConfig::optimizeForHugePage(maxSmallArrayTypeId, [](size_t type_id) noexcept { return type_id; }, hugePageSize, smallPageSize, sizeof(int), EntryRefType::offsetSize(), - minNumArraysForNewBuffer, + min_num_entries_for_new_buffer, ALLOC_GROW_FACTOR)) { } - void assertSpec(uint32_t type_id, uint32_t numArraysForNewBuffer) { + void assertSpec(uint32_t type_id, uint32_t num_entries_for_new_buffer) { assertSpec(type_id, AllocSpec(0, EntryRefType::offsetSize(), - numArraysForNewBuffer, ALLOC_GROW_FACTOR)); + num_entries_for_new_buffer, ALLOC_GROW_FACTOR)); } void assertSpec(uint32_t type_id, const AllocSpec &expSpec) { const auto& actSpec = cfg.spec_for_type_id(type_id); - EXPECT_EQUAL(expSpec.minArraysInBuffer, actSpec.minArraysInBuffer); - EXPECT_EQUAL(expSpec.maxArraysInBuffer, actSpec.maxArraysInBuffer); - EXPECT_EQUAL(expSpec.numArraysForNewBuffer, actSpec.numArraysForNewBuffer); + EXPECT_EQUAL(expSpec.min_entries_in_buffer, actSpec.min_entries_in_buffer); + EXPECT_EQUAL(expSpec.max_entries_in_buffer, actSpec.max_entries_in_buffer); + EXPECT_EQUAL(expSpec.num_entries_for_new_buffer, actSpec.num_entries_for_new_buffer); EXPECT_EQUAL(expSpec.allocGrowFactor, actSpec.allocGrowFactor); } }; AllocSpec -makeSpec(size_t minArraysInBuffer, - size_t maxArraysInBuffer, - size_t numArraysForNewBuffer) +makeSpec(size_t min_entries_in_buffer, + size_t max_entries_in_buffer, + size_t num_entries_for_new_buffer) { - return AllocSpec(minArraysInBuffer, maxArraysInBuffer, numArraysForNewBuffer, ALLOC_GROW_FACTOR); + return AllocSpec(min_entries_in_buffer, max_entries_in_buffer, num_entries_for_new_buffer, ALLOC_GROW_FACTOR); } constexpr size_t KB = 1024; diff --git a/vespalib/src/tests/datastore/buffer_stats/buffer_stats_test.cpp b/vespalib/src/tests/datastore/buffer_stats/buffer_stats_test.cpp index 09b2590a5f3..fec8d5949f8 100644 --- a/vespalib/src/tests/datastore/buffer_stats/buffer_stats_test.cpp +++ b/vespalib/src/tests/datastore/buffer_stats/buffer_stats_test.cpp @@ -9,10 +9,10 @@ using namespace vespalib::datastore; TEST(BufferStatsTest, buffer_stats_to_memory_stats) { InternalBufferStats buf; - buf.set_alloc_elems(17); + buf.set_alloc_entries(17); buf.pushed_back(7); - buf.set_dead_elems(5); - buf.set_hold_elems(3); + buf.set_dead_entries(5); + buf.set_hold_entries(3); buf.inc_extra_used_bytes(13); buf.inc_extra_hold_bytes(11); @@ -20,10 +20,10 @@ TEST(BufferStatsTest, buffer_stats_to_memory_stats) constexpr size_t es = 8; buf.add_to_mem_stats(es, mem); - EXPECT_EQ(17, mem._allocElems); - EXPECT_EQ(7, mem._usedElems); - EXPECT_EQ(5, mem._deadElems); - EXPECT_EQ(3, mem._holdElems); + EXPECT_EQ(17, mem._alloc_entries); + EXPECT_EQ(7, mem._used_entries); + EXPECT_EQ(5, mem._dead_entries); + EXPECT_EQ(3, mem._hold_entries); EXPECT_EQ(17 * es + 13, mem._allocBytes); EXPECT_EQ(7 * es + 13, mem._usedBytes); EXPECT_EQ(5 * es, mem._deadBytes); diff --git a/vespalib/src/tests/datastore/buffer_type/buffer_type_test.cpp b/vespalib/src/tests/datastore/buffer_type/buffer_type_test.cpp index de7d899e68a..9f7535a3676 100644 --- a/vespalib/src/tests/datastore/buffer_type/buffer_type_test.cpp +++ b/vespalib/src/tests/datastore/buffer_type/buffer_type_test.cpp @@ -7,40 +7,40 @@ using namespace vespalib::datastore; using IntBufferType = BufferType<int>; constexpr uint32_t ARRAYS_SIZE(4); -constexpr uint32_t MAX_ARRAYS(128); -constexpr uint32_t NUM_ARRAYS_FOR_NEW_BUFFER(0); +constexpr uint32_t MAX_ENTRIES(128); +constexpr uint32_t NUM_ENTRIES_FOR_NEW_BUFFER(0); struct Setup { - uint32_t _minArrays; - std::atomic<ElemCount> _usedElems; - ElemCount _neededElems; - std::atomic<ElemCount> _deadElems; + uint32_t _min_entries; + std::atomic<EntryCount> _used_entries; + EntryCount _needed_entries; + std::atomic<EntryCount> _dead_entries; uint32_t _bufferId; float _allocGrowFactor; bool _resizing; Setup() - : _minArrays(0), - _usedElems(0), - _neededElems(0), - _deadElems(0), + : _min_entries(0), + _used_entries(0), + _needed_entries(0), + _dead_entries(0), _bufferId(1), _allocGrowFactor(0.5), _resizing(false) {} Setup(const Setup& rhs) noexcept; - Setup &minArrays(uint32_t value) { _minArrays = value; return *this; } - Setup &used(size_t value) { _usedElems = value; return *this; } - Setup &needed(size_t value) { _neededElems = value; return *this; } - Setup &dead(size_t value) { _deadElems = value; return *this; } + Setup &min_entries(uint32_t value) { _min_entries = value; return *this; } + Setup &used(size_t value) { _used_entries = value; return *this; } + Setup &needed(size_t value) { _needed_entries = value; return *this; } + Setup &dead(size_t value) { _dead_entries = value; return *this; } Setup &bufferId(uint32_t value) { _bufferId = value; return *this; } Setup &resizing(bool value) { _resizing = value; return *this; } }; Setup::Setup(const Setup& rhs) noexcept - : _minArrays(rhs._minArrays), - _usedElems(rhs._usedElems.load(std::memory_order_relaxed)), - _neededElems(rhs._neededElems), - _deadElems(rhs._deadElems.load(std::memory_order_relaxed)), + : _min_entries(rhs._min_entries), + _used_entries(rhs._used_entries.load(std::memory_order_relaxed)), + _needed_entries(rhs._needed_entries), + _dead_entries(rhs._dead_entries.load(std::memory_order_relaxed)), _bufferId(rhs._bufferId), _allocGrowFactor(rhs._allocGrowFactor), _resizing(rhs._resizing) @@ -53,7 +53,7 @@ struct Fixture { int buffer[ARRAYS_SIZE]; Fixture(const Setup &setup_) : setups(), - bufferType(ARRAYS_SIZE, setup_._minArrays, MAX_ARRAYS, NUM_ARRAYS_FOR_NEW_BUFFER, setup_._allocGrowFactor), + bufferType(ARRAYS_SIZE, setup_._min_entries, MAX_ENTRIES, NUM_ENTRIES_FOR_NEW_BUFFER, setup_._allocGrowFactor), buffer() { setups.reserve(4); @@ -61,121 +61,121 @@ struct Fixture { } ~Fixture() { for (auto& setup : setups) { - bufferType.onHold(setup._bufferId, &setup._usedElems, &setup._deadElems); - bufferType.onFree(setup._usedElems); + bufferType.on_hold(setup._bufferId, &setup._used_entries, &setup._dead_entries); + bufferType.on_free(setup._used_entries); } } Setup& curr_setup() { return setups.back(); } void add_setup(const Setup& setup_in) { - // The buffer type stores pointers to ElemCount (from Setup) and we must ensure these do not move in memory. + // The buffer type stores pointers to EntryCount (from Setup) and we must ensure these do not move in memory. assert(setups.size() < setups.capacity()); setups.push_back(setup_in); } void onActive() { - bufferType.onActive(curr_setup()._bufferId, &curr_setup()._usedElems, &curr_setup()._deadElems, &buffer[0]); + bufferType.on_active(curr_setup()._bufferId, &curr_setup()._used_entries, &curr_setup()._dead_entries, &buffer[0]); } - size_t arraysToAlloc() { - return bufferType.calcArraysToAlloc(curr_setup()._bufferId, curr_setup()._neededElems, curr_setup()._resizing); + size_t entries_to_alloc() { + return bufferType.calc_entries_to_alloc(curr_setup()._bufferId, curr_setup()._needed_entries, curr_setup()._resizing); } - void assertArraysToAlloc(size_t exp) { + void assert_entries_to_alloc(size_t exp) { onActive(); - EXPECT_EQUAL(exp, arraysToAlloc()); + EXPECT_EQUAL(exp, entries_to_alloc()); } }; void -assertArraysToAlloc(size_t exp, const Setup &setup) +assert_entries_to_alloc(size_t exp, const Setup &setup) { Fixture f(setup); - f.assertArraysToAlloc(exp); + f.assert_entries_to_alloc(exp); } -TEST("require that complete arrays are allocated") +TEST("require that entries are allocated") { - TEST_DO(assertArraysToAlloc(1, Setup().needed(1))); - TEST_DO(assertArraysToAlloc(1, Setup().needed(2))); - TEST_DO(assertArraysToAlloc(1, Setup().needed(3))); - TEST_DO(assertArraysToAlloc(1, Setup().needed(4))); - TEST_DO(assertArraysToAlloc(2, Setup().needed(5))); + TEST_DO(assert_entries_to_alloc(1, Setup().needed(1))); + TEST_DO(assert_entries_to_alloc(2, Setup().needed(2))); + TEST_DO(assert_entries_to_alloc(3, Setup().needed(3))); + TEST_DO(assert_entries_to_alloc(4, Setup().needed(4))); + TEST_DO(assert_entries_to_alloc(5, Setup().needed(5))); } -TEST("require that reserved elements are taken into account when not resizing") +TEST("require that reserved entries are taken into account when not resizing") { - TEST_DO(assertArraysToAlloc(2, Setup().needed(1).bufferId(0))); - TEST_DO(assertArraysToAlloc(2, Setup().needed(4).bufferId(0))); - TEST_DO(assertArraysToAlloc(3, Setup().needed(5).bufferId(0))); + TEST_DO(assert_entries_to_alloc(2, Setup().needed(1).bufferId(0))); + TEST_DO(assert_entries_to_alloc(5, Setup().needed(4).bufferId(0))); + TEST_DO(assert_entries_to_alloc(6, Setup().needed(5).bufferId(0))); } -TEST("require that arrays to alloc is based on currently used elements (no resizing)") +TEST("require that entries to alloc is based on currently used entries (no resizing)") { - TEST_DO(assertArraysToAlloc(2, Setup().used(4 * 4).needed(4))); - TEST_DO(assertArraysToAlloc(4, Setup().used(8 * 4).needed(4))); + TEST_DO(assert_entries_to_alloc(2, Setup().used(4).needed(1))); + TEST_DO(assert_entries_to_alloc(4, Setup().used(8).needed(1))); } -TEST("require that arrays to alloc is based on currently used elements (with resizing)") +TEST("require that entries to alloc is based on currently used entries (with resizing)") { - TEST_DO(assertArraysToAlloc(4 + 2, Setup().used(4 * 4).needed(4).resizing(true))); - TEST_DO(assertArraysToAlloc(8 + 4, Setup().used(8 * 4).needed(4).resizing(true))); - TEST_DO(assertArraysToAlloc(4 + 3, Setup().used(4 * 4).needed(3 * 4).resizing(true))); + TEST_DO(assert_entries_to_alloc(4 + 2, Setup().used(4).needed(1).resizing(true))); + TEST_DO(assert_entries_to_alloc(8 + 4, Setup().used(8).needed(1).resizing(true))); + TEST_DO(assert_entries_to_alloc(4 + 3, Setup().used(4).needed(3).resizing(true))); } -TEST("require that arrays to alloc always contain elements needed") +TEST("require that entries to alloc always contain entries needed") { - TEST_DO(assertArraysToAlloc(2, Setup().used(4 * 4).needed(2 * 4))); - TEST_DO(assertArraysToAlloc(3, Setup().used(4 * 4).needed(3 * 4))); - TEST_DO(assertArraysToAlloc(4, Setup().used(4 * 4).needed(4 * 4))); + TEST_DO(assert_entries_to_alloc(2, Setup().used(4).needed(2))); + TEST_DO(assert_entries_to_alloc(3, Setup().used(4).needed(3))); + TEST_DO(assert_entries_to_alloc(4, Setup().used(4).needed(4))); } -TEST("require that arrays to alloc is capped to max arrays") +TEST("require that entries to alloc is capped to max entries") { - TEST_DO(assertArraysToAlloc(127, Setup().used(254 * 4).needed(4))); - TEST_DO(assertArraysToAlloc(128, Setup().used(256 * 4).needed(4))); - TEST_DO(assertArraysToAlloc(128, Setup().used(258 * 4).needed(8))); + TEST_DO(assert_entries_to_alloc(127, Setup().used(254).needed(1))); + TEST_DO(assert_entries_to_alloc(128, Setup().used(256).needed(1))); + TEST_DO(assert_entries_to_alloc(128, Setup().used(258).needed(2))); } TEST("require that arrays to alloc is capped to min arrays") { - TEST_DO(assertArraysToAlloc(16, Setup().used(30 * 4).needed(4).minArrays(16))); - TEST_DO(assertArraysToAlloc(16, Setup().used(32 * 4).needed(4).minArrays(16))); - TEST_DO(assertArraysToAlloc(17, Setup().used(34 * 4).needed(4).minArrays(16))); + TEST_DO(assert_entries_to_alloc(16, Setup().used(30).needed(1).min_entries(16))); + TEST_DO(assert_entries_to_alloc(16, Setup().used(32).needed(1).min_entries(16))); + TEST_DO(assert_entries_to_alloc(17, Setup().used(34).needed(1).min_entries(16))); } -TEST("arrays to alloc considers used elements across all active buffers of same type (no resizing)") +TEST("entries to alloc considers used entries across all active buffers of same type (no resizing)") { - Fixture f(Setup().used(6 * 4)); - f.assertArraysToAlloc(6 * 0.5); - f.add_setup(Setup().used(8 * 4).bufferId(2)); - f.assertArraysToAlloc((6 + 8) * 0.5); - f.add_setup(Setup().used(10 * 4).bufferId(3)); - f.assertArraysToAlloc((6 + 8 + 10) * 0.5); + Fixture f(Setup().used(6)); + f.assert_entries_to_alloc(6 * 0.5); + f.add_setup(Setup().used(8).bufferId(2)); + f.assert_entries_to_alloc((6 + 8) * 0.5); + f.add_setup(Setup().used(10).bufferId(3)); + f.assert_entries_to_alloc((6 + 8 + 10) * 0.5); } -TEST("arrays to alloc considers used elements across all active buffers of same type when resizing") +TEST("entries to alloc considers used entries across all active buffers of same type when resizing") { - Fixture f(Setup().used(6 * 4)); - f.assertArraysToAlloc(6 * 0.5); - f.add_setup(Setup().used(8 * 4).resizing(true).bufferId(2)); - f.assertArraysToAlloc(8 + (6 + 8) * 0.5); + Fixture f(Setup().used(6)); + f.assert_entries_to_alloc(6 * 0.5); + f.add_setup(Setup().used(8).resizing(true).bufferId(2)); + f.assert_entries_to_alloc(8 + (6 + 8) * 0.5); } -TEST("arrays to alloc considers (and subtracts) dead elements across all active buffers of same type (no resizing)") +TEST("entries to alloc considers (and subtracts) dead entries across all active buffers of same type (no resizing)") { - Fixture f(Setup().used(6 * 4).dead(2 * 4)); - f.assertArraysToAlloc((6 - 2) * 0.5); - f.add_setup(Setup().used(12 * 4).dead(4 * 4).bufferId(2)); - f.assertArraysToAlloc((6 - 2 + 12 - 4) * 0.5); - f.add_setup(Setup().used(20 * 4).dead(6 * 4).bufferId(3)); - f.assertArraysToAlloc((6 - 2 + 12 - 4 + 20 - 6) * 0.5); + Fixture f(Setup().used(6).dead(2)); + f.assert_entries_to_alloc((6 - 2) * 0.5); + f.add_setup(Setup().used(12).dead(4).bufferId(2)); + f.assert_entries_to_alloc((6 - 2 + 12 - 4) * 0.5); + f.add_setup(Setup().used(20).dead(6).bufferId(3)); + f.assert_entries_to_alloc((6 - 2 + 12 - 4 + 20 - 6) * 0.5); } TEST("arrays to alloc considers (and subtracts) dead elements across all active buffers of same type when resizing") { - Fixture f(Setup().used(6 * 4).dead(2 * 4)); - f.assertArraysToAlloc((6 - 2) * 0.5); - f.add_setup(Setup().used(12 * 4).dead(4 * 4).resizing(true).bufferId(2)); - f.assertArraysToAlloc(12 + (6 - 2 + 12 - 4) * 0.5); + Fixture f(Setup().used(6).dead(2)); + f.assert_entries_to_alloc((6 - 2) * 0.5); + f.add_setup(Setup().used(12).dead(4).resizing(true).bufferId(2)); + f.assert_entries_to_alloc(12 + (6 - 2 + 12 - 4) * 0.5); } TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/datastore/datastore/datastore_test.cpp b/vespalib/src/tests/datastore/datastore/datastore_test.cpp index df347267c7e..9e27ed37dd3 100644 --- a/vespalib/src/tests/datastore/datastore/datastore_test.cpp +++ b/vespalib/src/tests/datastore/datastore/datastore_test.cpp @@ -26,8 +26,8 @@ public: void holdBuffer(uint32_t bufferId) { ParentType::holdBuffer(bufferId); } - void holdElem(EntryRef ref, uint64_t len) { - ParentType::holdElem(ref, len); + void hold_entry(EntryRef ref) { + ParentType::hold_entry(ref); } void assign_generation(generation_t current_gen) { ParentType::assign_generation(current_gen); @@ -35,8 +35,8 @@ public: void reclaim_entry_refs(generation_t oldest_used_gen) override { ParentType::reclaim_entry_refs(oldest_used_gen); } - void ensureBufferCapacity(size_t sizeNeeded) { - ParentType::ensureBufferCapacity(0, sizeNeeded); + void ensure_buffer_capacity(size_t entries_needed) { + ParentType::ensure_buffer_capacity(0, entries_needed); } void enableFreeLists() { ParentType::enableFreeLists(); @@ -66,10 +66,10 @@ class GrowStore BufferType<DataType> _type; uint32_t _typeId; public: - GrowStore(size_t arraySize, size_t minArrays, size_t maxArrays, size_t numArraysForNewBuffer) + GrowStore(size_t arraySize, size_t min_entries, size_t max_entries, size_t num_entries_for_new_buffer) : _store(), - _firstType(1, 1, maxArrays, 0, ALLOC_GROW_FACTOR), - _type(arraySize, minArrays, maxArrays, numArraysForNewBuffer, ALLOC_GROW_FACTOR), + _firstType(1, 1, max_entries, 0, ALLOC_GROW_FACTOR), + _type(arraySize, min_entries, max_entries, num_entries_for_new_buffer, ALLOC_GROW_FACTOR), _typeId(0) { (void) _store.addType(&_firstType); @@ -87,7 +87,7 @@ public: while (sizes.size() < bufs) { RefType iRef = (_type.getArraySize() == 1) ? (_store.template allocator<DataType>(_typeId).alloc().ref) : - (_store.template allocator<DataType>(_typeId).allocArray(_type.getArraySize()).ref); + (_store.template allocator<DataType>(_typeId).allocArray().ref); int bufferId = iRef.bufferId(); if (bufferId != prevBufferId) { if (prevBufferId >= 0) { @@ -126,7 +126,7 @@ public: while (buffers.size() < bufs) { RefType iRef = (_type.getArraySize() == 1) ? (_store.template allocator<DataType>(_typeId).alloc().ref) : - (_store.template allocator<DataType>(_typeId).allocArray(_type.getArraySize()).ref); + (_store.template allocator<DataType>(_typeId).allocArray().ref); int buffer_id = iRef.bufferId(); if (buffers.empty() || buffers.back() != buffer_id) { buffers.push_back(buffer_id); @@ -143,10 +143,10 @@ void assertMemStats(const MemoryStats &exp, const MemoryStats &act) { - EXPECT_EQ(exp._allocElems, act._allocElems); - EXPECT_EQ(exp._usedElems, act._usedElems); - EXPECT_EQ(exp._deadElems, act._deadElems); - EXPECT_EQ(exp._holdElems, act._holdElems); + EXPECT_EQ(exp._alloc_entries, act._alloc_entries); + EXPECT_EQ(exp._used_entries, act._used_entries); + EXPECT_EQ(exp._dead_entries, act._dead_entries); + EXPECT_EQ(exp._hold_entries, act._hold_entries); EXPECT_EQ(exp._freeBuffers, act._freeBuffers); EXPECT_EQ(exp._activeBuffers, act._activeBuffers); EXPECT_EQ(exp._holdBuffers, act._holdBuffers); @@ -304,13 +304,13 @@ TEST(DataStoreTest, require_that_we_can_hold_and_trim_elements) { MyStore s; MyRef r1 = s.addEntry(1); - s.holdElem(r1, 1); + s.hold_entry(r1); s.assign_generation(10); MyRef r2 = s.addEntry(2); - s.holdElem(r2, 1); + s.hold_entry(r2); s.assign_generation(20); MyRef r3 = s.addEntry(3); - s.holdElem(r3, 1); + s.hold_entry(r3); s.assign_generation(30); EXPECT_EQ(1, s.getEntry(r1)); EXPECT_EQ(2, s.getEntry(r2)); @@ -358,11 +358,11 @@ TEST(DataStoreTest, require_that_we_can_use_free_lists) MyStore s; s.enableFreeLists(); auto r1 = s.addEntry(1); - s.holdElem(r1, 1); + s.hold_entry(r1); s.assign_generation(10); auto r2 = s.addEntry(2); expect_successive_refs(r1, r2); - s.holdElem(r2, 1); + s.hold_entry(r2); s.assign_generation(20); s.reclaim_entry_refs(11); auto r3 = s.addEntry(3); // reuse r1 @@ -389,21 +389,21 @@ TEST(DataStoreTest, require_that_we_can_use_free_lists_with_raw_allocator) s.enableFreeLists(); auto allocator = s.freeListRawAllocator<int>(grow_store.typeId()); - auto h1 = allocator.alloc(3); - auto h2 = allocator.alloc(3); + auto h1 = allocator.alloc(1); + auto h2 = allocator.alloc(1); expect_successive_handles(h1, h2); - s.holdElem(h1.ref, 3); - s.holdElem(h2.ref, 3); + s.hold_entry(h1.ref); + s.hold_entry(h2.ref); s.assign_generation(10); s.reclaim_entry_refs(11); - auto h3 = allocator.alloc(3); // reuse h2.ref from free list + auto h3 = allocator.alloc(1); // reuse h2.ref from free list EXPECT_EQ(h2, h3); - auto h4 = allocator.alloc(3); // reuse h1.ref from free list + auto h4 = allocator.alloc(1); // reuse h1.ref from free list EXPECT_EQ(h1, h4); - auto h5 = allocator.alloc(3); + auto h5 = allocator.alloc(1); expect_successive_handles(h2, h5); expect_successive_handles(h3, h5); } @@ -412,10 +412,10 @@ TEST(DataStoreTest, require_that_memory_stats_are_calculated) { MyStore s; MemoryStats m; - m._allocElems = MyRef::offsetSize(); - m._usedElems = 1; // ref = 0 is reserved - m._deadElems = 1; // ref = 0 is reserved - m._holdElems = 0; + m._alloc_entries = MyRef::offsetSize(); + m._used_entries = 1; // ref = 0 is reserved + m._dead_entries = 1; // ref = 0 is reserved + m._hold_entries = 0; m._activeBuffers = 1; m._freeBuffers = MyRef::numBuffers() - 1; m._holdBuffers = 0; @@ -423,7 +423,7 @@ TEST(DataStoreTest, require_that_memory_stats_are_calculated) // add entry MyRef r = s.addEntry(10); - m._usedElems++; + m._used_entries++; assertMemStats(m, s.getMemStats()); // hold buffer @@ -431,9 +431,9 @@ TEST(DataStoreTest, require_that_memory_stats_are_calculated) s.addEntry(30); s.holdBuffer(r.bufferId()); s.assign_generation(100); - m._usedElems += 2; - m._holdElems = m._usedElems; - m._deadElems = 0; + m._used_entries += 2; + m._hold_entries = m._used_entries; + m._dead_entries = 0; m._activeBuffers--; m._holdBuffers++; assertMemStats(m, s.getMemStats()); @@ -441,17 +441,17 @@ TEST(DataStoreTest, require_that_memory_stats_are_calculated) // new active buffer s.switch_primary_buffer(); s.addEntry(40); - m._allocElems += MyRef::offsetSize(); - m._usedElems++; + m._alloc_entries += MyRef::offsetSize(); + m._used_entries++; m._activeBuffers++; m._freeBuffers--; // trim hold buffer s.reclaim_memory(101); - m._allocElems -= MyRef::offsetSize(); - m._usedElems = 1; - m._deadElems = 0; - m._holdElems = 0; + m._alloc_entries -= MyRef::offsetSize(); + m._used_entries = 1; + m._dead_entries = 0; + m._hold_entries = 0; m._freeBuffers = MyRef::numBuffers() - 1; m._holdBuffers = 0; assertMemStats(m, s.getMemStats()); @@ -466,7 +466,7 @@ TEST(DataStoreTest, require_that_memory_stats_are_calculated) { // increase extra hold bytes auto prev_stats = s.getMemStats(); - s.get_active_buffer_state().hold_elems(0, 30); + s.get_active_buffer_state().hold_entries(0, 30); auto curr_stats = s.getMemStats(); EXPECT_EQ(prev_stats._holdBytes + 30, curr_stats._holdBytes); } @@ -475,7 +475,7 @@ TEST(DataStoreTest, require_that_memory_stats_are_calculated) TEST(DataStoreTest, require_that_memory_usage_is_calculated) { constexpr size_t BASE_ALLOCATED = 4228; - constexpr size_t BASE_USED = 308; + constexpr size_t BASE_USED = 284; MyStore s; MyRef r = s.addEntry(10); s.addEntry(20); @@ -494,7 +494,7 @@ TEST(DataStoreTest, require_that_memory_usage_is_calculated) TEST(DataStoreTest, require_that_we_can_disable_elemement_hold_list) { constexpr size_t BASE_ALLOCATED = 4228; - constexpr size_t BASE_USED = 308; + constexpr size_t BASE_USED = 284; MyStore s; MyRef r1 = s.addEntry(10); MyRef r2 = s.addEntry(20); @@ -505,14 +505,14 @@ TEST(DataStoreTest, require_that_we_can_disable_elemement_hold_list) EXPECT_EQ(4 * sizeof(int) + BASE_USED, m.usedBytes()); EXPECT_EQ(1 * sizeof(int), m.deadBytes()); EXPECT_EQ(0 * sizeof(int), m.allocatedBytesOnHold()); - s.holdElem(r1, 1); + s.hold_entry(r1); m = s.getMemoryUsage(); EXPECT_EQ(MyRef::offsetSize() * sizeof(int) + BASE_ALLOCATED, m.allocatedBytes()); EXPECT_EQ(4 * sizeof(int) + BASE_USED, m.usedBytes()); EXPECT_EQ(1 * sizeof(int), m.deadBytes()); EXPECT_EQ(1 * sizeof(int), m.allocatedBytesOnHold()); - s.disableElemHoldList(); - s.holdElem(r2, 1); + s.disable_entry_hold_list(); + s.hold_entry(r2); m = s.getMemoryUsage(); EXPECT_EQ(MyRef::offsetSize() * sizeof(int) + BASE_ALLOCATED, m.allocatedBytes()); EXPECT_EQ(4 * sizeof(int) + BASE_USED, m.usedBytes()); @@ -529,11 +529,11 @@ namespace { void assertGrowStats(GrowthStats expSizes, GrowthStats expFirstBufSizes, size_t expInitMemUsage, - size_t minArrays, size_t numArraysForNewBuffer, size_t maxArrays = 128) + size_t min_entries, size_t num_entries_for_new_buffer, size_t max_entries = 128) { - EXPECT_EQ(expSizes, IntGrowStore(1, minArrays, maxArrays, numArraysForNewBuffer).getGrowthStats(expSizes.size())); - EXPECT_EQ(expFirstBufSizes, IntGrowStore(1, minArrays, maxArrays, numArraysForNewBuffer).getFirstBufGrowStats()); - EXPECT_EQ(expInitMemUsage, IntGrowStore(1, minArrays, maxArrays, numArraysForNewBuffer).getMemoryUsage().allocatedBytes()); + EXPECT_EQ(expSizes, IntGrowStore(1, min_entries, max_entries, num_entries_for_new_buffer).getGrowthStats(expSizes.size())); + EXPECT_EQ(expFirstBufSizes, IntGrowStore(1, min_entries, max_entries, num_entries_for_new_buffer).getFirstBufGrowStats()); + EXPECT_EQ(expInitMemUsage, IntGrowStore(1, min_entries, max_entries, num_entries_for_new_buffer).getMemoryUsage().allocatedBytes()); } } @@ -574,10 +574,10 @@ namespace { template <typename DataType> void assertGrowStats(GrowthStats expSizes, uint32_t arraySize) { - uint32_t minArrays = 2048; - uint32_t maxArrays = RefType15::offsetSize(); - uint32_t numArraysForNewBuffer = 2048; - GrowStore<DataType, RefType15> store(arraySize, minArrays, maxArrays, numArraysForNewBuffer); + uint32_t min_entries = 2048; + uint32_t max_entries = RefType15::offsetSize(); + uint32_t num_entries_for_new_buffer = 2048; + GrowStore<DataType, RefType15> store(arraySize, min_entries, max_entries, num_entries_for_new_buffer); EXPECT_EQ(expSizes, store.getGrowthStats(expSizes.size())); } @@ -594,14 +594,14 @@ TEST(DataStoreTest, require_that_offset_in_EntryRefT_is_within_bounds_when_alloc * 3) Round up bytes to alloc to match the underlying allocator (power of 2 if less than huge page size): * After this we might end up with more bytes than the offset in EntryRef can handle. In this case this is 32768. * 4) Cap bytes to alloc to the max offset EntryRef can handle. - * The max bytes to alloc is: maxArrays * arraySize * elementSize. + * The max bytes to alloc is: max_entries * arraySize * elementSize. */ - assertGrowStats<uint8_t>({8192,16384,16384,65536,65536,98304,98304,98304,98304,98304,98304,98304}, 3); - assertGrowStats<uint8_t>({16384,16384,65536,65536,131072,131072,163840,163840,163840,163840,163840,163840}, 5); - assertGrowStats<uint8_t>({16384,32768,32768,131072,131072,229376,229376,229376,229376,229376,229376,229376}, 7); - assertGrowStats<uint32_t>({8192,16384,16384,65536,65536,98304,98304,98304,98304,98304,98304,98304}, 3); - assertGrowStats<uint32_t>({16384,16384,65536,65536,131072,131072,163840,163840,163840,163840,163840,163840}, 5); - assertGrowStats<uint32_t>({16384,32768,32768,131072,131072,229376,229376,229376,229376,229376,229376,229376}, 7); + assertGrowStats<uint8_t>({2730,5461,5461,21845,21845,32768,32768,32768,32768,32768,32768,32768}, 3); + assertGrowStats<uint8_t>({3276,3276,13107,13107,26214,26214,32768,32768,32768,32768,32768,32768}, 5); + assertGrowStats<uint8_t>({2340,4681,4681,18724,18724,32768,32768,32768,32768,32768,32768,32768}, 7); + assertGrowStats<uint32_t>({2730,5461,5461,21845,21845,32768,32768,32768,32768,32768,32768,32768}, 3); + assertGrowStats<uint32_t>({3276,3276,13107,13107,26214,26214,32768,32768,32768,32768,32768,32768}, 5); + assertGrowStats<uint32_t>({2340,4681,4681,18724,18724,32768,32768,32768,32768,32768,32768,32768}, 7); } namespace { @@ -667,9 +667,9 @@ TEST(DataStoreTest, can_reuse_active_buffer_as_primary_buffer) TEST(DataStoreTest, control_static_sizes) { EXPECT_EQ(88, sizeof(BufferTypeBase)); EXPECT_EQ(24, sizeof(FreeList)); - EXPECT_EQ(56, sizeof(BufferFreeList)); + EXPECT_EQ(48, sizeof(BufferFreeList)); EXPECT_EQ(1, sizeof(BufferState::State)); - EXPECT_EQ(144, sizeof(BufferState)); + EXPECT_EQ(120, sizeof(BufferState)); BufferState bs; EXPECT_EQ(0, bs.size()); } @@ -685,11 +685,11 @@ void test_free_element_to_held_buffer(bool before_hold_buffer) EXPECT_EQ(1u, s.primary_buffer_id()); if (before_hold_buffer) { - s.holdElem(ref, 1); + s.hold_entry(ref); } s.holdBuffer(0); // hold last buffer if (!before_hold_buffer) { - ASSERT_DEATH({ s.holdElem(ref, 1); }, "isActive\\(\\)"); + ASSERT_DEATH({ s.hold_entry(ref); }, "isActive\\(\\)"); } s.assign_generation(100); s.reclaim_memory(101); diff --git a/vespalib/src/tests/datastore/free_list/free_list_test.cpp b/vespalib/src/tests/datastore/free_list/free_list_test.cpp index 44e11b2316b..ec14d0dd28c 100644 --- a/vespalib/src/tests/datastore/free_list/free_list_test.cpp +++ b/vespalib/src/tests/datastore/free_list/free_list_test.cpp @@ -8,20 +8,18 @@ using namespace vespalib::datastore; using MyEntryRef = EntryRefT<8, 4>; -constexpr uint32_t array_size = 6; struct FreeListTest : public testing::Test { FreeList list; - std::atomic<ElemCount> dead_elems; + std::atomic<EntryCount> dead_entries; std::vector<BufferFreeList> bufs; FreeListTest() : list(), bufs() { for (size_t i = 0; i < 3; ++i) { - bufs.emplace_back(dead_elems); - bufs.back().set_array_size(array_size); + bufs.emplace_back(dead_entries); } } void TearDown() override { @@ -126,13 +124,13 @@ TEST_F(FreeListTest, buffer_free_list_can_be_disabled_and_detached_when_not_curr EXPECT_TRUE(list.empty()); } -TEST_F(FreeListTest, dead_elems_count_is_updated_when_popping_an_entry) +TEST_F(FreeListTest, dead_entries_count_is_updated_when_popping_an_entry) { enable(0); push_entry({10, 0}); - dead_elems.store(18, std::memory_order_relaxed); + dead_entries.store(18, std::memory_order_relaxed); pop_entry(); - EXPECT_EQ(18 - array_size, dead_elems.load(std::memory_order_relaxed)); + EXPECT_EQ(17, dead_entries.load(std::memory_order_relaxed)); } GTEST_MAIN_RUN_ALL_TESTS() diff --git a/vespalib/src/tests/datastore/unique_store/unique_store_test.cpp b/vespalib/src/tests/datastore/unique_store/unique_store_test.cpp index 5ccf9a8908c..a09a7213bf5 100644 --- a/vespalib/src/tests/datastore/unique_store/unique_store_test.cpp +++ b/vespalib/src/tests/datastore/unique_store/unique_store_test.cpp @@ -96,8 +96,8 @@ struct TestBase : public ::testing::Test { } void assertBufferState(EntryRef ref, const TestBufferStats expStats) const { EXPECT_EQ(expStats._used, store.bufferState(ref).size()); - EXPECT_EQ(expStats._hold, store.bufferState(ref).stats().hold_elems()); - EXPECT_EQ(expStats._dead, store.bufferState(ref).stats().dead_elems()); + EXPECT_EQ(expStats._hold, store.bufferState(ref).stats().hold_entries()); + EXPECT_EQ(expStats._dead, store.bufferState(ref).stats().dead_entries()); } void assertStoreContent() const { for (const auto &elem : refStore) { @@ -147,10 +147,7 @@ struct TestBase : public ::testing::Test { auto getBuilder(uint32_t uniqueValuesHint) { return store.getBuilder(uniqueValuesHint); } auto getEnumerator(bool sort_unique_values) { return store.getEnumerator(sort_unique_values); } size_t get_reserved(EntryRef ref) { - return store.bufferState(ref).getTypeHandler()->getReservedElements(getBufferId(ref)); - } - size_t get_array_size(EntryRef ref) { - return store.bufferState(ref).getArraySize(); + return store.bufferState(ref).getTypeHandler()->get_reserved_entries(getBufferId(ref)); } }; @@ -309,29 +306,27 @@ TYPED_TEST(TestBase, can_add_and_get_values) } } -TYPED_TEST(TestBase, elements_are_put_on_hold_when_value_is_removed) +TYPED_TEST(TestBase, entries_are_put_on_hold_when_value_is_removed) { EntryRef ref = this->add(this->values()[0]); size_t reserved = this->get_reserved(ref); - size_t array_size = this->get_array_size(ref); - this->assertBufferState(ref, TestBufferStats().used(array_size + reserved).hold(0).dead(reserved)); + this->assertBufferState(ref, TestBufferStats().used(1 + reserved).hold(0).dead(reserved)); this->store.remove(ref); - this->assertBufferState(ref, TestBufferStats().used(array_size + reserved).hold(array_size).dead(reserved)); + this->assertBufferState(ref, TestBufferStats().used(1 + reserved).hold(1).dead(reserved)); } -TYPED_TEST(TestBase, elements_are_reference_counted) +TYPED_TEST(TestBase, entries_are_reference_counted) { EntryRef ref = this->add(this->values()[0]); EntryRef ref2 = this->add(this->values()[0]); EXPECT_EQ(ref.ref(), ref2.ref()); - // Note: The first buffer have the first element reserved -> we expect 2 elements used here. + // Note: The first buffer have the first entry reserved -> we expect 2 entries used here. size_t reserved = this->get_reserved(ref); - size_t array_size = this->get_array_size(ref); - this->assertBufferState(ref, TestBufferStats().used(array_size + reserved).hold(0).dead(reserved)); + this->assertBufferState(ref, TestBufferStats().used(1 + reserved).hold(0).dead(reserved)); this->store.remove(ref); - this->assertBufferState(ref, TestBufferStats().used(array_size + reserved).hold(0).dead(reserved)); + this->assertBufferState(ref, TestBufferStats().used(1 + reserved).hold(0).dead(reserved)); this->store.remove(ref); - this->assertBufferState(ref, TestBufferStats().used(array_size + reserved).hold(array_size).dead(reserved)); + this->assertBufferState(ref, TestBufferStats().used(1 + reserved).hold(1).dead(reserved)); } TEST_F(SmallOffsetNumberTest, new_underlying_buffer_is_allocated_when_current_is_full) @@ -360,8 +355,7 @@ TYPED_TEST(TestBase, store_can_be_compacted) this->remove(this->add(this->values()[2])); this->reclaim_memory(); size_t reserved = this->get_reserved(val0Ref); - size_t array_size = this->get_array_size(val0Ref); - this->assertBufferState(val0Ref, TestBufferStats().used(reserved + 3 * array_size).dead(reserved + array_size)); + this->assertBufferState(val0Ref, TestBufferStats().used(reserved + 3).dead(reserved + 1)); uint32_t val1BufferId = this->getBufferId(val0Ref); EXPECT_EQ(2u, this->refStore.size()); @@ -389,8 +383,7 @@ TYPED_TEST(TestBase, store_can_be_instantiated_with_builder) EntryRef val0Ref = builder.mapEnumValueToEntryRef(1); EntryRef val1Ref = builder.mapEnumValueToEntryRef(2); size_t reserved = this->get_reserved(val0Ref); - size_t array_size = this->get_array_size(val0Ref); - this->assertBufferState(val0Ref, TestBufferStats().used(2 * array_size + reserved).dead(reserved)); // Note: First element is reserved + this->assertBufferState(val0Ref, TestBufferStats().used(2 + reserved).dead(reserved)); // Note: First entry is reserved EXPECT_TRUE(val0Ref.valid()); EXPECT_TRUE(val1Ref.valid()); EXPECT_NE(val0Ref.ref(), val1Ref.ref()); @@ -472,13 +465,13 @@ TEST_F(DoubleTest, nan_is_handled) TEST_F(DoubleTest, control_memory_usage) { static constexpr size_t sizeof_deque = vespalib::datastore::DataStoreBase::sizeof_entry_ref_hold_list_deque; EXPECT_EQ(368u + sizeof_deque, sizeof(store)); - EXPECT_EQ(144u, sizeof(BufferState)); + EXPECT_EQ(120u, sizeof(BufferState)); EXPECT_EQ(28740u, store.get_values_memory_usage().allocatedBytes()); - EXPECT_EQ(24804u, store.get_values_memory_usage().usedBytes()); + EXPECT_EQ(24780u, store.get_values_memory_usage().usedBytes()); EXPECT_EQ(126952u, store.get_dictionary_memory_usage().allocatedBytes()); - EXPECT_EQ(25248u, store.get_dictionary_memory_usage().usedBytes()); + EXPECT_EQ(25200u, store.get_dictionary_memory_usage().usedBytes()); EXPECT_EQ(155692u, store.getMemoryUsage().allocatedBytes()); - EXPECT_EQ(50052, store.getMemoryUsage().usedBytes()); + EXPECT_EQ(49980u, store.getMemoryUsage().usedBytes()); } GTEST_MAIN_RUN_ALL_TESTS() diff --git a/vespalib/src/tests/datastore/unique_store_string_allocator/unique_store_string_allocator_test.cpp b/vespalib/src/tests/datastore/unique_store_string_allocator/unique_store_string_allocator_test.cpp index 7d4451556c8..8ea7f807f56 100644 --- a/vespalib/src/tests/datastore/unique_store_string_allocator/unique_store_string_allocator_test.cpp +++ b/vespalib/src/tests/datastore/unique_store_string_allocator/unique_store_string_allocator_test.cpp @@ -62,8 +62,8 @@ struct TestBase : public ::testing::Test { void assert_buffer_state(EntryRef ref, const TestBufferStats expStats) { auto & stats = buffer_state(ref).stats(); EXPECT_EQ(expStats._used, buffer_state(ref).size()); - EXPECT_EQ(expStats._hold, stats.hold_elems()); - EXPECT_EQ(expStats._dead, stats.dead_elems()); + EXPECT_EQ(expStats._hold, stats.hold_entries()); + EXPECT_EQ(expStats._dead, stats.dead_entries()); EXPECT_EQ(expStats._extra_used, stats.extra_used_bytes()); EXPECT_EQ(expStats._extra_hold, stats.extra_hold_bytes()); } @@ -83,14 +83,14 @@ TEST_F(StringTest, can_add_and_get_values) assert_add(spaces1000.c_str()); } -TEST_F(StringTest, elements_are_put_on_hold_when_value_is_removed) +TEST_F(StringTest, entries_are_put_on_hold_when_value_is_removed) { EntryRef ref = add(small.c_str()); - assert_buffer_state(ref, TestBufferStats().used(16).hold(0).dead(0)); + assert_buffer_state(ref, TestBufferStats().used(1).hold(0).dead(0)); remove(ref); - assert_buffer_state(ref, TestBufferStats().used(16).hold(16).dead(0)); + assert_buffer_state(ref, TestBufferStats().used(1).hold(1).dead(0)); reclaim_memory(); - assert_buffer_state(ref, TestBufferStats().used(16).hold(0).dead(16)); + assert_buffer_state(ref, TestBufferStats().used(1).hold(0).dead(1)); } TEST_F(StringTest, extra_bytes_used_is_tracked) @@ -139,7 +139,7 @@ TEST_F(StringTest, free_list_is_used_when_enabled) EntryRef ref4 = add(spaces1000.c_str()); EXPECT_EQ(ref1, ref3); EXPECT_EQ(ref2, ref4); - assert_buffer_state(ref1, TestBufferStats().used(16).hold(0).dead(0)); + assert_buffer_state(ref1, TestBufferStats().used(1).hold(0).dead(0)); assert_buffer_state(ref2, TestBufferStats().used(2).hold(0).dead(1).extra_used(1001)); } @@ -155,7 +155,7 @@ TEST_F(StringTest, free_list_is_not_used_when_disabled) EntryRef ref4 = add(spaces1000.c_str()); EXPECT_NE(ref1, ref3); EXPECT_NE(ref2, ref4); - assert_buffer_state(ref1, TestBufferStats().used(32).hold(0).dead(16)); + assert_buffer_state(ref1, TestBufferStats().used(2).hold(0).dead(1)); assert_buffer_state(ref2, TestBufferStats().used(3).hold(0).dead(2).extra_used(1001)); } @@ -173,7 +173,7 @@ TEST_F(StringTest, free_list_is_never_used_for_move_on_compact) EntryRef ref6 = move_on_compact(ref2); EXPECT_NE(ref5, ref3); EXPECT_NE(ref6, ref4); - assert_buffer_state(ref1, TestBufferStats().used(48).hold(0).dead(16)); + assert_buffer_state(ref1, TestBufferStats().used(3).hold(0).dead(1)); assert_buffer_state(ref2, TestBufferStats().used(4).hold(0).dead(2).extra_used(2002)); } diff --git a/vespalib/src/tests/signalhandler/CMakeLists.txt b/vespalib/src/tests/signalhandler/CMakeLists.txt index 4f78eb2e82d..88be14f994f 100644 --- a/vespalib/src/tests/signalhandler/CMakeLists.txt +++ b/vespalib/src/tests/signalhandler/CMakeLists.txt @@ -5,6 +5,11 @@ vespa_add_library(vespalib_signalhandler_test_my_shared_library TEST DEPENDS vespalib ) + +# Don't convert call to jump when returning a value from a function with +# a compatible stack. +set_source_files_properties(my_shared_library.cpp PROPERTIES COMPILE_OPTIONS "-fno-optimize-sibling-calls") + vespa_add_executable(vespalib_signalhandler_test_app TEST SOURCES signalhandler_test.cpp diff --git a/vespalib/src/vespa/vespalib/btree/btree.h b/vespalib/src/vespa/vespalib/btree/btree.h index 32b538b65ec..c2f5aac01b7 100644 --- a/vespalib/src/vespa/vespalib/btree/btree.h +++ b/vespalib/src/vespa/vespalib/btree/btree.h @@ -61,9 +61,9 @@ public: } void - disableElemHoldList() + disable_entry_hold_list() { - _alloc.disableElemHoldList(); + _alloc.disable_entry_hold_list(); } // Inherit doc from BTreeRoot diff --git a/vespalib/src/vespa/vespalib/btree/btreenodeallocator.h b/vespalib/src/vespa/vespalib/btree/btreenodeallocator.h index 784e95e3817..b537602c703 100644 --- a/vespalib/src/vespa/vespalib/btree/btreenodeallocator.h +++ b/vespalib/src/vespa/vespalib/btree/btreenodeallocator.h @@ -60,8 +60,8 @@ public: _nodeStore.disableFreeLists(); } - void disableElemHoldList() { - _nodeStore.disableElemHoldList(); + void disable_entry_hold_list() { + _nodeStore.disable_entry_hold_list(); } /** diff --git a/vespalib/src/vespa/vespalib/btree/btreenodeallocator.hpp b/vespalib/src/vespa/vespalib/btree/btreenodeallocator.hpp index a38b68afe73..d23c8fc2054 100644 --- a/vespalib/src/vespa/vespalib/btree/btreenodeallocator.hpp +++ b/vespalib/src/vespa/vespalib/btree/btreenodeallocator.hpp @@ -162,7 +162,7 @@ holdNode(BTreeNode::Ref nodeRef, InternalNodeType *node) { if (node->getFrozen()) { - _nodeStore.holdElem(nodeRef); + _nodeStore.hold_entry(nodeRef); } else { node->clean(); _internalHoldUntilFreeze.push_back(nodeRef); @@ -178,7 +178,7 @@ holdNode(BTreeNode::Ref nodeRef, LeafNodeType *node) { if (node->getFrozen()) { - _nodeStore.holdElem(nodeRef); + _nodeStore.hold_entry(nodeRef); } else { node->clean(); _leafHoldUntilFreeze.push_back(nodeRef); @@ -235,7 +235,7 @@ freeze() InternalNodeType *inode = mapInternalRef(i); (void) inode; assert(inode->getFrozen()); - _nodeStore.holdElem(i); + _nodeStore.hold_entry(i); } _internalHoldUntilFreeze.clear(); } @@ -245,7 +245,7 @@ freeze() LeafNodeType *lnode = mapLeafRef(i); (void) lnode; assert(lnode->getFrozen()); - _nodeStore.holdElem(i); + _nodeStore.hold_entry(i); } _leafHoldUntilFreeze.clear(); } diff --git a/vespalib/src/vespa/vespalib/btree/btreenodestore.h b/vespalib/src/vespa/vespalib/btree/btreenodestore.h index c59092bf75c..38bf4e5ed4e 100644 --- a/vespalib/src/vespa/vespalib/btree/btreenodestore.h +++ b/vespalib/src/vespa/vespalib/btree/btreenodestore.h @@ -29,16 +29,16 @@ class BTreeNodeBufferType : public datastore::BufferType<EntryType, FrozenBtreeN using ParentType = datastore::BufferType<EntryType, FrozenBtreeNode<EntryType>>; using ParentType::empty_entry; using ParentType::_arraySize; - using ElemCount = typename ParentType::ElemCount; + using EntryCount = typename ParentType::EntryCount; using CleanContext = typename ParentType::CleanContext; public: - BTreeNodeBufferType(uint32_t minArrays, uint32_t maxArrays) - : ParentType(1, minArrays, maxArrays) + BTreeNodeBufferType(uint32_t min_entries, uint32_t max_entries) + : ParentType(1, min_entries, max_entries) { } - void initializeReservedElements(void *buffer, ElemCount reservedElements) override; + void initialize_reserved_entries(void *buffer, EntryCount reserved_entries) override; - void cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) override; + void clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) override; }; @@ -79,7 +79,7 @@ public: ~BTreeNodeStore(); void disableFreeLists() { _store.disableFreeLists(); } - void disableElemHoldList() { _store.disableElemHoldList(); } + void disable_entry_hold_list() { _store.disable_entry_hold_list(); } static bool isValidRef(EntryRef ref) { return ref.valid(); } @@ -152,8 +152,8 @@ public: return _store.freeListAllocator<InternalNodeType, BTreeNodeReclaimer>(NODETYPE_INTERNAL).alloc(rhs); } - void holdElem(EntryRef ref) { - _store.holdElem(ref, 1); + void hold_entry(EntryRef ref) { + _store.hold_entry(ref); } std::unique_ptr<vespalib::datastore::CompactingBuffers> start_compact_worst(const CompactionStrategy& compaction_strategy); diff --git a/vespalib/src/vespa/vespalib/btree/btreenodestore.hpp b/vespalib/src/vespa/vespalib/btree/btreenodestore.hpp index a1ffb4d445d..99054f35d61 100644 --- a/vespalib/src/vespa/vespalib/btree/btreenodestore.hpp +++ b/vespalib/src/vespa/vespalib/btree/btreenodestore.hpp @@ -11,11 +11,11 @@ namespace vespalib::btree { template <typename EntryType> void -BTreeNodeBufferType<EntryType>::initializeReservedElements(void *buffer, ElemCount reservedElements) +BTreeNodeBufferType<EntryType>::initialize_reserved_entries(void *buffer, EntryCount reserved_entries) { - ParentType::initializeReservedElements(buffer, reservedElements); + ParentType::initialize_reserved_entries(buffer, reserved_entries); EntryType *e = static_cast<EntryType *>(buffer); - for (size_t j = reservedElements; j != 0; --j) { + for (size_t j = reserved_entries; j != 0; --j) { e->freeze(); ++e; } @@ -24,10 +24,10 @@ BTreeNodeBufferType<EntryType>::initializeReservedElements(void *buffer, ElemCou template <typename EntryType> void -BTreeNodeBufferType<EntryType>::cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext) +BTreeNodeBufferType<EntryType>::clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext) { EntryType *e = static_cast<EntryType *>(buffer) + offset; - for (size_t j = numElems; j != 0; --j) { + for (size_t j = num_entries; j != 0; --j) { e->cleanFrozen(); ++e; } diff --git a/vespalib/src/vespa/vespalib/btree/btreestore.h b/vespalib/src/vespa/vespalib/btree/btreestore.h index 9fdade850f1..7dd839f1529 100644 --- a/vespalib/src/vespa/vespalib/btree/btreestore.h +++ b/vespalib/src/vespa/vespalib/btree/btreestore.h @@ -50,8 +50,8 @@ public: using CompactionSpec = datastore::CompactionSpec; using CompactionStrategy = datastore::CompactionStrategy; using EntryRef = datastore::EntryRef; - template <typename EntryType> - using BufferType = datastore::BufferType<EntryType>; + template <typename ElemT> + using BufferType = datastore::BufferType<ElemT>; using BufferState = datastore::BufferState; static constexpr uint32_t clusterLimit = 8; @@ -105,9 +105,9 @@ public: _allocator.disableFreeLists(); } - void disableElemHoldList() { - _store.disableElemHoldList(); - _allocator.disableElemHoldList(); + void disable_entry_hold_list() { + _store.disable_entry_hold_list(); + _allocator.disable_entry_hold_list(); } BTreeTypeRefPair allocNewBTree() { diff --git a/vespalib/src/vespa/vespalib/btree/btreestore.hpp b/vespalib/src/vespa/vespalib/btree/btreestore.hpp index a19d0b34aa6..90c302af5e4 100644 --- a/vespalib/src/vespa/vespalib/btree/btreestore.hpp +++ b/vespalib/src/vespa/vespalib/btree/btreestore.hpp @@ -74,7 +74,7 @@ allocNewKeyData(uint32_t clusterSize) { assert(clusterSize >= 1 && clusterSize <= clusterLimit); uint32_t typeId = clusterSize - 1; - return _store.allocator<KeyDataType>(typeId).allocArray(clusterSize); + return _store.allocator<KeyDataType>(typeId).allocArray(); } @@ -87,7 +87,7 @@ allocKeyData(uint32_t clusterSize) { assert(clusterSize >= 1 && clusterSize <= clusterLimit); uint32_t typeId = clusterSize - 1; - return _store.freeListAllocator<KeyDataType, datastore::DefaultReclaimer<KeyDataType>>(typeId).allocArray(clusterSize); + return _store.freeListAllocator<KeyDataType, datastore::DefaultReclaimer<KeyDataType>>(typeId).allocArray(); } @@ -156,7 +156,7 @@ makeTree(EntryRef &ref, lNode->freeze(); BTreeTypeRefPair tPair(allocBTree()); tPair.data->setRoots(lPair.ref); - _store.holdElem(ref, clusterSize); + _store.hold_entry(ref); ref = tPair.ref; } @@ -176,7 +176,7 @@ makeArray(EntryRef &ref, EntryRef root, LeafNodeType *leafNode) kd->setData(leafNode->getData(idx)); } assert(kd == kPair.data + clusterSize); - _store.holdElem(ref, 1); + _store.hold_entry(ref); if (!leafNode->getFrozen()) { leafNode->freeze(); } @@ -255,7 +255,7 @@ insert(EntryRef &ref, kd->setData(i->getData()); } assert(kd == kPair.data + clusterSize + 1); - _store.holdElem(ref, clusterSize); + _store.hold_entry(ref); ref = kPair.ref; return true; } @@ -284,7 +284,7 @@ insert(EntryRef &ref, lNode->freeze(); BTreeTypeRefPair tPair(allocBTree()); tPair.data->setRoots(lPair.ref); // allow immediate access to readers - _store.holdElem(ref, clusterSize); + _store.hold_entry(ref); ref = tPair.ref; return true; #endif @@ -339,7 +339,7 @@ remove(EntryRef &ref, if (oldi == olde || comp(key, oldi->_key)) return false; // not found if (clusterSize == 1) { - _store.holdElem(ref, 1); + _store.hold_entry(ref); ref = EntryRef(); return true; } @@ -357,7 +357,7 @@ remove(EntryRef &ref, kd->setData(i->getData()); } assert(kd == kPair.data + clusterSize - 1); - _store.holdElem(ref, clusterSize); + _store.hold_entry(ref); ref = kPair.ref; return true; } @@ -670,7 +670,7 @@ applyCluster(EntryRef &ref, if (newSizeMin <= clusterLimit) { uint32_t newSize = getNewClusterSize(ob, oe, a, ae, r, re, comp); if (newSize == 0) { - _store.holdElem(ref, clusterSize); + _store.hold_entry(ref); ref = EntryRef(); return true; } @@ -678,7 +678,7 @@ applyCluster(EntryRef &ref, KeyDataTypeRefPair kPair(allocKeyData(newSize)); applyCluster(ob, oe, kPair.data, kPair.data + newSize, a, ae, r, re, comp); - _store.holdElem(ref, clusterSize); + _store.hold_entry(ref); ref = kPair.ref; return true; } @@ -735,7 +735,7 @@ normalizeTree(EntryRef &ref, { EntryRef root = tree->getRoot(); if (!NodeAllocatorType::isValidRef(root)) { - _store.holdElem(ref, 1); + _store.hold_entry(ref); ref = EntryRef(); return; } @@ -798,10 +798,8 @@ clear(const EntryRef ref) if (clusterSize == 0) { BTreeType *tree = getWTreeEntry(iRef); tree->clear(_allocator); - _store.holdElem(ref, 1); - } else { - _store.holdElem(ref, clusterSize); } + _store.hold_entry(ref); } diff --git a/vespalib/src/vespa/vespalib/datastore/allocator.h b/vespalib/src/vespa/vespalib/datastore/allocator.h index 297270af0f5..30938bdc1c1 100644 --- a/vespalib/src/vespa/vespalib/datastore/allocator.h +++ b/vespalib/src/vespa/vespalib/datastore/allocator.h @@ -30,7 +30,7 @@ public: HandleType alloc(Args && ... args); HandleType allocArray(ConstArrayRef array); - HandleType allocArray(size_t size); + HandleType allocArray(); }; } diff --git a/vespalib/src/vespa/vespalib/datastore/allocator.hpp b/vespalib/src/vespa/vespalib/datastore/allocator.hpp index 85f0e842519..fa97ef9a5f5 100644 --- a/vespalib/src/vespa/vespalib/datastore/allocator.hpp +++ b/vespalib/src/vespa/vespalib/datastore/allocator.hpp @@ -20,7 +20,7 @@ template <typename ... Args> typename Allocator<EntryT, RefT>::HandleType Allocator<EntryT, RefT>::alloc(Args && ... args) { - _store.ensureBufferCapacity(_typeId, 1); + _store.ensure_buffer_capacity(_typeId, 1); uint32_t buffer_id = _store.primary_buffer_id(_typeId); BufferState &state = _store.getBufferState(buffer_id); assert(state.isActive()); @@ -36,39 +36,35 @@ template <typename EntryT, typename RefT> typename Allocator<EntryT, RefT>::HandleType Allocator<EntryT, RefT>::allocArray(ConstArrayRef array) { - _store.ensureBufferCapacity(_typeId, array.size()); + _store.ensure_buffer_capacity(_typeId, 1); uint32_t buffer_id = _store.primary_buffer_id(_typeId); BufferState &state = _store.getBufferState(buffer_id); assert(state.isActive()); assert(state.getArraySize() == array.size()); - size_t oldBufferSize = state.size(); - assert((oldBufferSize % array.size()) == 0); - RefT ref((oldBufferSize / array.size()), buffer_id); + RefT ref(state.size(), buffer_id); EntryT *buf = _store.template getEntryArray<EntryT>(ref, array.size()); for (size_t i = 0; i < array.size(); ++i) { new (static_cast<void *>(buf + i)) EntryT(array[i]); } - state.stats().pushed_back(array.size()); + state.stats().pushed_back(1); return HandleType(ref, buf); } template <typename EntryT, typename RefT> typename Allocator<EntryT, RefT>::HandleType -Allocator<EntryT, RefT>::allocArray(size_t size) +Allocator<EntryT, RefT>::allocArray() { - _store.ensureBufferCapacity(_typeId, size); + _store.ensure_buffer_capacity(_typeId, 1); uint32_t buffer_id = _store.primary_buffer_id(_typeId); BufferState &state = _store.getBufferState(buffer_id); assert(state.isActive()); - assert(state.getArraySize() == size); - size_t oldBufferSize = state.size(); - assert((oldBufferSize % size) == 0); - RefT ref((oldBufferSize / size), buffer_id); - EntryT *buf = _store.template getEntryArray<EntryT>(ref, size); - for (size_t i = 0; i < size; ++i) { + RefT ref(state.size(), buffer_id); + auto array_size = state.getArraySize(); + EntryT *buf = _store.template getEntryArray<EntryT>(ref, array_size); + for (size_t i = 0; i < array_size; ++i) { new (static_cast<void *>(buf + i)) EntryT(); } - state.stats().pushed_back(size); + state.stats().pushed_back(1); return HandleType(ref, buf); } diff --git a/vespalib/src/vespa/vespalib/datastore/array_store.h b/vespalib/src/vespa/vespalib/datastore/array_store.h index c2b65d72f03..809ac10f6e3 100644 --- a/vespalib/src/vespa/vespalib/datastore/array_store.h +++ b/vespalib/src/vespa/vespalib/datastore/array_store.h @@ -19,7 +19,7 @@ namespace vespalib::datastore { /** - * Datastore for storing arrays of type EntryT that is accessed via a 32-bit EntryRef. + * Datastore for storing arrays of type ElemT that is accessed via a 32-bit EntryRef. * * The default EntryRef type uses 19 bits for offset (524288 values) and 13 bits for buffer id (8192 buffers). * @@ -29,16 +29,18 @@ namespace vespalib::datastore { * * The max value of maxSmallArrayTypeId is (2^bufferBits - 1). */ -template <typename EntryT, typename RefT = EntryRefT<19>, typename TypeMapperT = ArrayStoreSimpleTypeMapper<EntryT> > +template <typename ElemT, typename RefT = EntryRefT<19>, typename TypeMapperT = ArrayStoreSimpleTypeMapper<ElemT> > class ArrayStore : public ICompactable { public: using AllocSpec = ArrayStoreConfig::AllocSpec; - using ArrayRef = vespalib::ArrayRef<EntryT>; - using ConstArrayRef = vespalib::ConstArrayRef<EntryT>; + using ArrayRef = vespalib::ArrayRef<ElemT>; + using ConstArrayRef = vespalib::ConstArrayRef<ElemT>; using DataStoreType = DataStoreT<RefT>; - using LargeArray = vespalib::Array<EntryT>; + using ElemType = ElemT; + using LargeArray = vespalib::Array<ElemT>; using LargeBufferType = typename TypeMapperT::LargeBufferType; + using RefType = RefT; using SmallBufferType = typename TypeMapperT::SmallBufferType; using TypeMapper = TypeMapperT; private: @@ -58,7 +60,7 @@ private: EntryRef addLargeArray(const ConstArrayRef &array); EntryRef allocate_large_array(size_t array_size); ConstArrayRef getSmallArray(RefT ref, size_t arraySize) const { - const EntryT *buf = _store.template getEntryArray<EntryT>(ref, arraySize); + const ElemT *buf = _store.template getEntryArray<ElemT>(ref, arraySize); return ConstArrayRef(buf, arraySize); } ConstArrayRef getLargeArray(RefT ref) const { @@ -85,7 +87,7 @@ public: } /** - * Allocate an array of the given size without any instantiation of EntryT elements. + * Allocate an array of the given size without any instantiation of ElemT elements. * * Use get_writable() to get a reference to the array for writing. * @@ -148,14 +150,14 @@ public: static ArrayStoreConfig optimizedConfigForHugePage(uint32_t maxSmallArrayTypeId, size_t hugePageSize, size_t smallPageSize, - size_t minNumArraysForNewBuffer, + size_t min_num_entries_for_new_buffer, float allocGrowFactor); static ArrayStoreConfig optimizedConfigForHugePage(uint32_t maxSmallArrayTypeId, const TypeMapper& mapper, size_t hugePageSize, size_t smallPageSize, - size_t minNumArraysForNewBuffer, + size_t min_num_entries_for_new_buffer, float allocGrowFactor); }; diff --git a/vespalib/src/vespa/vespalib/datastore/array_store.hpp b/vespalib/src/vespa/vespalib/datastore/array_store.hpp index 301cff1e414..8e9fe779ba9 100644 --- a/vespalib/src/vespa/vespalib/datastore/array_store.hpp +++ b/vespalib/src/vespa/vespalib/datastore/array_store.hpp @@ -15,9 +15,9 @@ namespace vespalib::datastore { -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> void -ArrayStore<EntryT, RefT, TypeMapperT>::initArrayTypes(const ArrayStoreConfig &cfg, std::shared_ptr<alloc::MemoryAllocator> memory_allocator) +ArrayStore<ElemT, RefT, TypeMapperT>::initArrayTypes(const ArrayStoreConfig &cfg, std::shared_ptr<alloc::MemoryAllocator> memory_allocator) { _largeArrayTypeId = _store.addType(&_largeArrayType); assert(_largeArrayTypeId == 0); @@ -31,14 +31,14 @@ ArrayStore<EntryT, RefT, TypeMapperT>::initArrayTypes(const ArrayStoreConfig &cf } } -template <typename EntryT, typename RefT, typename TypeMapperT> -ArrayStore<EntryT, RefT, TypeMapperT>::ArrayStore(const ArrayStoreConfig &cfg, std::shared_ptr<alloc::MemoryAllocator> memory_allocator) +template <typename ElemT, typename RefT, typename TypeMapperT> +ArrayStore<ElemT, RefT, TypeMapperT>::ArrayStore(const ArrayStoreConfig &cfg, std::shared_ptr<alloc::MemoryAllocator> memory_allocator) : ArrayStore(cfg, memory_allocator, TypeMapper()) { } -template <typename EntryT, typename RefT, typename TypeMapperT> -ArrayStore<EntryT, RefT, TypeMapperT>::ArrayStore(const ArrayStoreConfig &cfg, std::shared_ptr<alloc::MemoryAllocator> memory_allocator, +template <typename ElemT, typename RefT, typename TypeMapperT> +ArrayStore<ElemT, RefT, TypeMapperT>::ArrayStore(const ArrayStoreConfig &cfg, std::shared_ptr<alloc::MemoryAllocator> memory_allocator, TypeMapper&& mapper) : _largeArrayTypeId(0), _maxSmallArrayTypeId(cfg.maxSmallArrayTypeId()), @@ -56,25 +56,25 @@ ArrayStore<EntryT, RefT, TypeMapperT>::ArrayStore(const ArrayStoreConfig &cfg, s } } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> vespalib::MemoryUsage -ArrayStore<EntryT, RefT, TypeMapperT>::getMemoryUsage() const { +ArrayStore<ElemT, RefT, TypeMapperT>::getMemoryUsage() const { vespalib::MemoryUsage usage = _store.getMemoryUsage(); usage.incAllocatedBytes(_smallArrayTypes.capacity() * sizeof(SmallBufferType)); usage.incUsedBytes(_smallArrayTypes.size() * sizeof(SmallBufferType)); return usage; } -template <typename EntryT, typename RefT, typename TypeMapperT> -ArrayStore<EntryT, RefT, TypeMapperT>::~ArrayStore() +template <typename ElemT, typename RefT, typename TypeMapperT> +ArrayStore<ElemT, RefT, TypeMapperT>::~ArrayStore() { _store.reclaim_all_memory(); _store.dropBuffers(); } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> EntryRef -ArrayStore<EntryT, RefT, TypeMapperT>::add(const ConstArrayRef &array) +ArrayStore<ElemT, RefT, TypeMapperT>::add(const ConstArrayRef &array) { if (array.size() == 0) { return EntryRef(); @@ -86,9 +86,9 @@ ArrayStore<EntryT, RefT, TypeMapperT>::add(const ConstArrayRef &array) } } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> EntryRef -ArrayStore<EntryT, RefT, TypeMapperT>::allocate(size_t array_size) +ArrayStore<ElemT, RefT, TypeMapperT>::allocate(size_t array_size) { if (array_size == 0) { return EntryRef(); @@ -100,94 +100,93 @@ ArrayStore<EntryT, RefT, TypeMapperT>::allocate(size_t array_size) } } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> EntryRef -ArrayStore<EntryT, RefT, TypeMapperT>::addSmallArray(const ConstArrayRef &array) +ArrayStore<ElemT, RefT, TypeMapperT>::addSmallArray(const ConstArrayRef &array) { uint32_t typeId = _mapper.get_type_id(array.size()); - using NoOpReclaimer = DefaultReclaimer<EntryT>; - return _store.template freeListAllocator<EntryT, NoOpReclaimer>(typeId).allocArray(array).ref; + using NoOpReclaimer = DefaultReclaimer<ElemT>; + return _store.template freeListAllocator<ElemT, NoOpReclaimer>(typeId).allocArray(array).ref; } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> EntryRef -ArrayStore<EntryT, RefT, TypeMapperT>::allocate_small_array(size_t array_size) +ArrayStore<ElemT, RefT, TypeMapperT>::allocate_small_array(size_t array_size) { uint32_t type_id = _mapper.get_type_id(array_size); - return _store.template freeListRawAllocator<EntryT>(type_id).alloc(array_size).ref; + return _store.template freeListRawAllocator<ElemT>(type_id).alloc(1).ref; } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> EntryRef -ArrayStore<EntryT, RefT, TypeMapperT>::addLargeArray(const ConstArrayRef &array) +ArrayStore<ElemT, RefT, TypeMapperT>::addLargeArray(const ConstArrayRef &array) { using NoOpReclaimer = DefaultReclaimer<LargeArray>; auto handle = _store.template freeListAllocator<LargeArray, NoOpReclaimer>(_largeArrayTypeId) .alloc(array.cbegin(), array.cend()); auto& state = _store.getBufferState(RefT(handle.ref).bufferId()); - state.stats().inc_extra_used_bytes(sizeof(EntryT) * array.size()); + state.stats().inc_extra_used_bytes(sizeof(ElemT) * array.size()); return handle.ref; } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> EntryRef -ArrayStore<EntryT, RefT, TypeMapperT>::allocate_large_array(size_t array_size) +ArrayStore<ElemT, RefT, TypeMapperT>::allocate_large_array(size_t array_size) { using NoOpReclaimer = DefaultReclaimer<LargeArray>; auto handle = _store.template freeListAllocator<LargeArray, NoOpReclaimer>(_largeArrayTypeId).alloc(array_size); auto& state = _store.getBufferState(RefT(handle.ref).bufferId()); - state.stats().inc_extra_used_bytes(sizeof(EntryT) * array_size); + state.stats().inc_extra_used_bytes(sizeof(ElemT) * array_size); return handle.ref; } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> void -ArrayStore<EntryT, RefT, TypeMapperT>::remove(EntryRef ref) +ArrayStore<ElemT, RefT, TypeMapperT>::remove(EntryRef ref) { if (ref.valid()) { RefT internalRef(ref); uint32_t typeId = _store.getTypeId(internalRef.bufferId()); if (typeId != _largeArrayTypeId) { - size_t arraySize = _mapper.get_array_size(typeId); - _store.holdElem(ref, arraySize); + _store.hold_entry(ref); } else { - _store.holdElem(ref, 1, sizeof(EntryT) * get(ref).size()); + _store.hold_entry(ref, sizeof(ElemT) * get(ref).size()); } } } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> EntryRef -ArrayStore<EntryT, RefT, TypeMapperT>::move_on_compact(EntryRef ref) +ArrayStore<ElemT, RefT, TypeMapperT>::move_on_compact(EntryRef ref) { return add(get(ref)); } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> ICompactionContext::UP -ArrayStore<EntryT, RefT, TypeMapperT>::compact_worst(const CompactionStrategy &compaction_strategy) +ArrayStore<ElemT, RefT, TypeMapperT>::compact_worst(const CompactionStrategy &compaction_strategy) { auto compacting_buffers = _store.start_compact_worst_buffers(_compaction_spec, compaction_strategy); return std::make_unique<CompactionContext>(*this, std::move(compacting_buffers)); } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> std::unique_ptr<CompactingBuffers> -ArrayStore<EntryT, RefT, TypeMapperT>::start_compact_worst_buffers(const CompactionStrategy &compaction_strategy) +ArrayStore<ElemT, RefT, TypeMapperT>::start_compact_worst_buffers(const CompactionStrategy &compaction_strategy) { return _store.start_compact_worst_buffers(_compaction_spec, compaction_strategy); } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> vespalib::AddressSpace -ArrayStore<EntryT, RefT, TypeMapperT>::addressSpaceUsage() const +ArrayStore<ElemT, RefT, TypeMapperT>::addressSpaceUsage() const { return _store.getAddressSpaceUsage(); } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> vespalib::MemoryUsage -ArrayStore<EntryT, RefT, TypeMapperT>::update_stat(const CompactionStrategy& compaction_strategy) +ArrayStore<ElemT, RefT, TypeMapperT>::update_stat(const CompactionStrategy& compaction_strategy) { auto address_space_usage = _store.getAddressSpaceUsage(); auto memory_usage = getMemoryUsage(); @@ -195,20 +194,20 @@ ArrayStore<EntryT, RefT, TypeMapperT>::update_stat(const CompactionStrategy& com return memory_usage; } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> const BufferState & -ArrayStore<EntryT, RefT, TypeMapperT>::bufferState(EntryRef ref) +ArrayStore<ElemT, RefT, TypeMapperT>::bufferState(EntryRef ref) { RefT internalRef(ref); return _store.getBufferState(internalRef.bufferId()); } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> ArrayStoreConfig -ArrayStore<EntryT, RefT, TypeMapperT>::optimizedConfigForHugePage(uint32_t maxSmallArrayTypeId, +ArrayStore<ElemT, RefT, TypeMapperT>::optimizedConfigForHugePage(uint32_t maxSmallArrayTypeId, size_t hugePageSize, size_t smallPageSize, - size_t minNumArraysForNewBuffer, + size_t min_num_entries_for_new_buffer, float allocGrowFactor) { TypeMapper mapper; @@ -216,26 +215,26 @@ ArrayStore<EntryT, RefT, TypeMapperT>::optimizedConfigForHugePage(uint32_t maxSm mapper, hugePageSize, smallPageSize, - minNumArraysForNewBuffer, + min_num_entries_for_new_buffer, allocGrowFactor); } -template <typename EntryT, typename RefT, typename TypeMapperT> +template <typename ElemT, typename RefT, typename TypeMapperT> ArrayStoreConfig -ArrayStore<EntryT, RefT, TypeMapperT>::optimizedConfigForHugePage(uint32_t maxSmallArrayTypeId, +ArrayStore<ElemT, RefT, TypeMapperT>::optimizedConfigForHugePage(uint32_t maxSmallArrayTypeId, const TypeMapper& mapper, size_t hugePageSize, size_t smallPageSize, - size_t minNumArraysForNewBuffer, + size_t min_num_entries_for_new_buffer, float allocGrowFactor) { return ArrayStoreConfig::optimizeForHugePage(mapper.get_max_small_array_type_id(maxSmallArrayTypeId), [&](uint32_t type_id) noexcept { return mapper.get_array_size(type_id); }, hugePageSize, smallPageSize, - sizeof(EntryT), + sizeof(ElemT), RefT::offsetSize(), - minNumArraysForNewBuffer, + min_num_entries_for_new_buffer, allocGrowFactor); } diff --git a/vespalib/src/vespa/vespalib/datastore/array_store_config.cpp b/vespalib/src/vespa/vespalib/datastore/array_store_config.cpp index d5587841745..1df9354cd6c 100644 --- a/vespalib/src/vespa/vespalib/datastore/array_store_config.cpp +++ b/vespalib/src/vespa/vespalib/datastore/array_store_config.cpp @@ -49,19 +49,19 @@ ArrayStoreConfig::optimizeForHugePage(uint32_t maxSmallArrayTypeId, std::function<size_t(uint32_t)> type_id_to_array_size, size_t hugePageSize, size_t smallPageSize, - size_t entrySize, + size_t elem_size, size_t maxEntryRefOffset, - size_t minNumArraysForNewBuffer, + size_t min_num_entries_for_new_buffer, float allocGrowFactor) { AllocSpecVector allocSpecs; - allocSpecs.emplace_back(0, maxEntryRefOffset, minNumArraysForNewBuffer, allocGrowFactor); // large array spec; + allocSpecs.emplace_back(0, maxEntryRefOffset, min_num_entries_for_new_buffer, allocGrowFactor); // large array spec; for (uint32_t type_id = 1; type_id <= maxSmallArrayTypeId; ++type_id) { size_t arraySize = type_id_to_array_size(type_id); - size_t numArraysForNewBuffer = hugePageSize / (entrySize * arraySize); - numArraysForNewBuffer = capToLimits(numArraysForNewBuffer, minNumArraysForNewBuffer, maxEntryRefOffset); - numArraysForNewBuffer = alignToSmallPageSize(numArraysForNewBuffer, minNumArraysForNewBuffer, smallPageSize); - allocSpecs.emplace_back(0, maxEntryRefOffset, numArraysForNewBuffer, allocGrowFactor); + size_t num_entries_for_new_buffer = hugePageSize / (elem_size * arraySize); + num_entries_for_new_buffer = capToLimits(num_entries_for_new_buffer, min_num_entries_for_new_buffer, maxEntryRefOffset); + num_entries_for_new_buffer = alignToSmallPageSize(num_entries_for_new_buffer, min_num_entries_for_new_buffer, smallPageSize); + allocSpecs.emplace_back(0, maxEntryRefOffset, num_entries_for_new_buffer, allocGrowFactor); } return ArrayStoreConfig(allocSpecs); } diff --git a/vespalib/src/vespa/vespalib/datastore/array_store_config.h b/vespalib/src/vespa/vespalib/datastore/array_store_config.h index a326c00d042..cae241dba10 100644 --- a/vespalib/src/vespa/vespalib/datastore/array_store_config.h +++ b/vespalib/src/vespa/vespalib/datastore/array_store_config.h @@ -19,21 +19,21 @@ public: * Specification of buffer allocation strategy for arrays of a given size. */ struct AllocSpec { - // Minimum number of arrays to allocate in a buffer. - size_t minArraysInBuffer; - // Maximum number of arrays to allocate in a buffer. - size_t maxArraysInBuffer; - // Number of arrays needed before allocating a new buffer instead of just resizing the first one. - size_t numArraysForNewBuffer; + // Minimum number of entries to allocate in a buffer. + size_t min_entries_in_buffer; + // Maximum number of entries to allocate in a buffer. + size_t max_entries_in_buffer; + // Number of entries needed before allocating a new buffer instead of just resizing the first one. + size_t num_entries_for_new_buffer; // Grow factor used when allocating a new buffer. float allocGrowFactor; - AllocSpec(size_t minArraysInBuffer_, - size_t maxArraysInBuffer_, - size_t numArraysForNewBuffer_, + AllocSpec(size_t min_entries_in_buffer_, + size_t max_entries_in_buffer_, + size_t num_entries_for_new_buffer_, float allocGrowFactor_) noexcept - : minArraysInBuffer(minArraysInBuffer_), - maxArraysInBuffer(maxArraysInBuffer_), - numArraysForNewBuffer(numArraysForNewBuffer_), + : min_entries_in_buffer(min_entries_in_buffer_), + max_entries_in_buffer(max_entries_in_buffer_), + num_entries_for_new_buffer(num_entries_for_new_buffer_), allocGrowFactor(allocGrowFactor_) {} }; @@ -76,9 +76,9 @@ public: std::function<size_t(uint32_t)> type_id_to_array_size, size_t hugePageSize, size_t smallPageSize, - size_t entrySize, + size_t elem_size, size_t maxEntryRefOffset, - size_t minNumArraysForNewBuffer, + size_t min_num_entries_for_new_buffer, float allocGrowFactor); }; diff --git a/vespalib/src/vespa/vespalib/datastore/array_store_simple_type_mapper.h b/vespalib/src/vespa/vespalib/datastore/array_store_simple_type_mapper.h index a0cd7827b2d..5f9728a43cc 100644 --- a/vespalib/src/vespa/vespalib/datastore/array_store_simple_type_mapper.h +++ b/vespalib/src/vespa/vespalib/datastore/array_store_simple_type_mapper.h @@ -15,11 +15,11 @@ namespace vespalib::datastore { * * A more complex mapping can be used by creating a custom mapper and BufferType implementations. */ -template <typename EntryT> +template <typename ElemT> class ArrayStoreSimpleTypeMapper { public: - using SmallBufferType = SmallArrayBufferType<EntryT>; - using LargeBufferType = LargeArrayBufferType<EntryT>; + using SmallBufferType = SmallArrayBufferType<ElemT>; + using LargeBufferType = LargeArrayBufferType<ElemT>; uint32_t get_type_id(size_t array_size) const { return array_size; } size_t get_array_size(uint32_t type_id) const { return type_id; } diff --git a/vespalib/src/vespa/vespalib/datastore/array_store_type_mapper.h b/vespalib/src/vespa/vespalib/datastore/array_store_type_mapper.h index e707627de19..2a406a39bf9 100644 --- a/vespalib/src/vespa/vespalib/datastore/array_store_type_mapper.h +++ b/vespalib/src/vespa/vespalib/datastore/array_store_type_mapper.h @@ -12,7 +12,7 @@ namespace vespalib::datastore { * This class provides mapping between type ids and array sizes needed for * storing a value with size smaller than or equal to the array size. * - * The array sizes vector is a monotic increasing sequence that might end + * The array sizes vector is a monotonic strictly increasing sequence that might end * with exponential growth. */ class ArrayStoreTypeMapper diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_free_list.cpp b/vespalib/src/vespa/vespalib/datastore/buffer_free_list.cpp index 224ed4b0c8f..851db0222c2 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_free_list.cpp +++ b/vespalib/src/vespa/vespalib/datastore/buffer_free_list.cpp @@ -20,9 +20,8 @@ BufferFreeList::detach() _free_list->detach(*this); } -BufferFreeList::BufferFreeList(std::atomic<ElemCount>& dead_elems) - : _dead_elems(dead_elems), - _array_size(0), +BufferFreeList::BufferFreeList(std::atomic<EntryCount>& dead_entries) + : _dead_entries(dead_entries), _free_list(), _free_refs() { @@ -66,7 +65,7 @@ BufferFreeList::pop_entry() { if (empty()) { detach(); } - _dead_elems.store(_dead_elems.load(std::memory_order_relaxed) - _array_size, std::memory_order_relaxed); + _dead_entries.store(_dead_entries.load(std::memory_order_relaxed) - 1, std::memory_order_relaxed); return ret; } diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_free_list.h b/vespalib/src/vespa/vespalib/datastore/buffer_free_list.h index 148ddd8db88..4348a41af04 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_free_list.h +++ b/vespalib/src/vespa/vespalib/datastore/buffer_free_list.h @@ -19,8 +19,7 @@ class BufferFreeList { private: using EntryRefArray = vespalib::Array<EntryRef>; - std::atomic<ElemCount>& _dead_elems; - uint32_t _array_size; + std::atomic<EntryCount>& _dead_entries; FreeList* _free_list; EntryRefArray _free_refs; @@ -28,7 +27,7 @@ private: void detach(); public: - BufferFreeList(std::atomic<ElemCount>& dead_elems); + BufferFreeList(std::atomic<EntryCount>& dead_entrie); ~BufferFreeList(); BufferFreeList(BufferFreeList&&) = default; // Needed for emplace_back() during setup. BufferFreeList(const BufferFreeList&) = delete; @@ -37,10 +36,8 @@ public: void enable(FreeList& free_list); void disable(); - void set_array_size(uint32_t value) { _array_size = value; } bool enabled() const { return _free_list != nullptr; } bool empty() const { return _free_refs.empty(); } - uint32_t array_size() const { return _array_size; } void push_entry(EntryRef ref); EntryRef pop_entry(); }; diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_stats.cpp b/vespalib/src/vespa/vespalib/datastore/buffer_stats.cpp index 8d97414626e..0d96e3f6d47 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_stats.cpp +++ b/vespalib/src/vespa/vespalib/datastore/buffer_stats.cpp @@ -6,27 +6,27 @@ namespace vespalib::datastore { BufferStats::BufferStats() - : _alloc_elems(0), - _used_elems(0), - _hold_elems(0), - _dead_elems(0), + : _alloc_entries(0), + _used_entries(0), + _hold_entries(0), + _dead_entries(0), _extra_used_bytes(0), _extra_hold_bytes(0) { } void -BufferStats::add_to_mem_stats(size_t element_size, MemoryStats& stats) const +BufferStats::add_to_mem_stats(size_t entry_size, MemoryStats& stats) const { size_t extra_used = extra_used_bytes(); - stats._allocElems += capacity(); - stats._usedElems += size(); - stats._deadElems += dead_elems(); - stats._holdElems += hold_elems(); - stats._allocBytes += (capacity() * element_size) + extra_used; - stats._usedBytes += (size() * element_size) + extra_used; - stats._deadBytes += dead_elems() * element_size; - stats._holdBytes += (hold_elems() * element_size) + extra_hold_bytes(); + stats._alloc_entries += capacity(); + stats._used_entries += size(); + stats._dead_entries += dead_entries(); + stats._hold_entries += hold_entries(); + stats._allocBytes += (capacity() * entry_size) + extra_used; + stats._usedBytes += (size() * entry_size) + extra_used; + stats._deadBytes += dead_entries() * entry_size; + stats._holdBytes += (hold_entries() * entry_size) + extra_hold_bytes(); } InternalBufferStats::InternalBufferStats() @@ -37,20 +37,20 @@ InternalBufferStats::InternalBufferStats() void InternalBufferStats::clear() { - _alloc_elems.store(0, std::memory_order_relaxed); - _used_elems.store(0, std::memory_order_relaxed); - _hold_elems.store(0, std::memory_order_relaxed); - _dead_elems.store(0, std::memory_order_relaxed); + _alloc_entries.store(0, std::memory_order_relaxed); + _used_entries.store(0, std::memory_order_relaxed); + _hold_entries.store(0, std::memory_order_relaxed); + _dead_entries.store(0, std::memory_order_relaxed); _extra_used_bytes.store(0, std::memory_order_relaxed); _extra_hold_bytes.store(0, std::memory_order_relaxed); } void -InternalBufferStats::dec_hold_elems(size_t value) +InternalBufferStats::dec_hold_entries(size_t value) { - ElemCount elems = hold_elems(); + EntryCount elems = hold_entries(); assert(elems >= value); - _hold_elems.store(elems - value, std::memory_order_relaxed); + _hold_entries.store(elems - value, std::memory_order_relaxed); } } diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_stats.h b/vespalib/src/vespa/vespalib/datastore/buffer_stats.h index 66f8b532c41..1974efa97ec 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_stats.h +++ b/vespalib/src/vespa/vespalib/datastore/buffer_stats.h @@ -13,16 +13,16 @@ namespace vespalib::datastore { */ class BufferStats { protected: - // The number of elements that are allocated in the buffer. - std::atomic<ElemCount> _alloc_elems; - // The number of elements (of the allocated) that are used: _used_elems <= _alloc_elems. - std::atomic<ElemCount> _used_elems; - // The number of elements (of the used) that are on hold: _hold_elems <= _used_elems. - // "On hold" is a transitionary state used when removing elements. - std::atomic<ElemCount> _hold_elems; - // The number of elements (of the used) that are dead: _dead_elems <= _used_elems. - // A dead element was first on hold, and is now available for reuse in the free list (if enabled). - std::atomic<ElemCount> _dead_elems; + // The number of entries that are allocated in the buffer. + std::atomic<EntryCount> _alloc_entries; + // The number of entries (of the allocated) that are used: _used_entries <= _alloc_entries. + std::atomic<EntryCount> _used_entries; + // The number of entries (of the used) that are on hold: _hold_entries <= _used_entries. + // "On hold" is a transitionary state used when removing entries. + std::atomic<EntryCount> _hold_entries; + // The number of entries (of the used) that are dead: _dead_entries <= _used_entries. + // A dead entry was first on hold, and is now available for reuse in the free list (if enabled). + std::atomic<EntryCount> _dead_entries; // Number of bytes that are heap allocated (and used) by elements that are stored in this buffer. // For simple types this is always 0. @@ -34,22 +34,22 @@ protected: public: BufferStats(); - size_t size() const { return _used_elems.load(std::memory_order_relaxed); } - size_t capacity() const { return _alloc_elems.load(std::memory_order_relaxed); } + size_t size() const { return _used_entries.load(std::memory_order_relaxed); } + size_t capacity() const { return _alloc_entries.load(std::memory_order_relaxed); } size_t remaining() const { return capacity() - size(); } - void pushed_back(size_t num_elems) { - _used_elems.store(size() + num_elems, std::memory_order_relaxed); + void pushed_back(size_t num_entries) { + _used_entries.store(size() + num_entries, std::memory_order_relaxed); } - size_t dead_elems() const { return _dead_elems.load(std::memory_order_relaxed); } - size_t hold_elems() const { return _hold_elems.load(std::memory_order_relaxed); } + size_t dead_entries() const { return _dead_entries.load(std::memory_order_relaxed); } + size_t hold_entries() const { return _hold_entries.load(std::memory_order_relaxed); } size_t extra_used_bytes() const { return _extra_used_bytes.load(std::memory_order_relaxed); } size_t extra_hold_bytes() const { return _extra_hold_bytes.load(std::memory_order_relaxed); } void inc_extra_used_bytes(size_t value) { _extra_used_bytes.store(extra_used_bytes() + value, std::memory_order_relaxed); } - void add_to_mem_stats(size_t element_size, MemoryStats& stats) const; + void add_to_mem_stats(size_t entry_size, MemoryStats& stats) const; }; /** @@ -59,15 +59,15 @@ class InternalBufferStats : public BufferStats { public: InternalBufferStats(); void clear(); - void set_alloc_elems(size_t value) { _alloc_elems.store(value, std::memory_order_relaxed); } - void set_dead_elems(size_t value) { _dead_elems.store(value, std::memory_order_relaxed); } - void set_hold_elems(size_t value) { _hold_elems.store(value, std::memory_order_relaxed); } - void inc_dead_elems(size_t value) { _dead_elems.store(dead_elems() + value, std::memory_order_relaxed); } - void inc_hold_elems(size_t value) { _hold_elems.store(hold_elems() + value, std::memory_order_relaxed); } - void dec_hold_elems(size_t value); + void set_alloc_entries(size_t value) { _alloc_entries.store(value, std::memory_order_relaxed); } + void set_dead_entries(size_t value) { _dead_entries.store(value, std::memory_order_relaxed); } + void set_hold_entries(size_t value) { _hold_entries.store(value, std::memory_order_relaxed); } + void inc_dead_entries(size_t value) { _dead_entries.store(dead_entries() + value, std::memory_order_relaxed); } + void inc_hold_entries(size_t value) { _hold_entries.store(hold_entries() + value, std::memory_order_relaxed); } + void dec_hold_entries(size_t value); void inc_extra_hold_bytes(size_t value) { _extra_hold_bytes.store(extra_hold_bytes() + value, std::memory_order_relaxed); } - std::atomic<ElemCount>& used_elems_ref() { return _used_elems; } - std::atomic<ElemCount>& dead_elems_ref() { return _dead_elems; } + std::atomic<EntryCount>& used_entries_ref() { return _used_entries; } + std::atomic<EntryCount>& dead_entries_ref() { return _dead_entries; } std::atomic<size_t>& extra_used_bytes_ref() { return _extra_used_bytes; } std::atomic<size_t>& extra_hold_bytes_ref() { return _extra_hold_bytes; } }; diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_type.cpp b/vespalib/src/vespa/vespalib/datastore/buffer_type.cpp index 4a9ba2d33a8..0d43ede9e62 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_type.cpp +++ b/vespalib/src/vespa/vespalib/datastore/buffer_type.cpp @@ -27,85 +27,85 @@ BufferTypeBase::CleanContext::extraBytesCleaned(size_t value) } BufferTypeBase::BufferTypeBase(uint32_t arraySize, - uint32_t minArrays, - uint32_t maxArrays, - uint32_t numArraysForNewBuffer, + uint32_t min_entries, + uint32_t max_entries, + uint32_t num_entries_for_new_buffer, float allocGrowFactor) noexcept : _arraySize(arraySize), - _minArrays(std::min(minArrays, maxArrays)), - _maxArrays(maxArrays), - _numArraysForNewBuffer(std::min(numArraysForNewBuffer, maxArrays)), + _min_entries(std::min(min_entries, max_entries)), + _max_entries(max_entries), + _num_entries_for_new_buffer(std::min(num_entries_for_new_buffer, max_entries)), _allocGrowFactor(allocGrowFactor), _holdBuffers(0), - _holdUsedElems(0), + _hold_used_entries(0), _aggr_counts(), _active_buffers() { } BufferTypeBase::BufferTypeBase(uint32_t arraySize, - uint32_t minArrays, - uint32_t maxArrays) noexcept - : BufferTypeBase(arraySize, minArrays, maxArrays, 0u, DEFAULT_ALLOC_GROW_FACTOR) + uint32_t min_entries, + uint32_t max_entries) noexcept + : BufferTypeBase(arraySize, min_entries, max_entries, 0u, DEFAULT_ALLOC_GROW_FACTOR) { } BufferTypeBase::~BufferTypeBase() { assert(_holdBuffers == 0); - assert(_holdUsedElems == 0); + assert(_hold_used_entries == 0); assert(_aggr_counts.empty()); assert(_active_buffers.empty()); } -ElemCount -BufferTypeBase::getReservedElements(uint32_t bufferId) const +EntryCount +BufferTypeBase::get_reserved_entries(uint32_t bufferId) const { - return bufferId == 0 ? _arraySize : 0u; + return bufferId == 0 ? 1u : 0u; } void -BufferTypeBase::onActive(uint32_t bufferId, std::atomic<ElemCount>* usedElems, std::atomic<ElemCount>* deadElems, void* buffer) +BufferTypeBase::on_active(uint32_t bufferId, std::atomic<EntryCount>* used_entries, std::atomic<EntryCount>* dead_entries, void* buffer) { - _aggr_counts.add_buffer(usedElems, deadElems); + _aggr_counts.add_buffer(used_entries, dead_entries); assert(std::find(_active_buffers.begin(), _active_buffers.end(), bufferId) == _active_buffers.end()); _active_buffers.emplace_back(bufferId); - size_t reservedElems = getReservedElements(bufferId); - if (reservedElems != 0u) { - initializeReservedElements(buffer, reservedElems); - *usedElems = reservedElems; - *deadElems = reservedElems; + auto reserved_entries = get_reserved_entries(bufferId); + if (reserved_entries != 0u) { + initialize_reserved_entries(buffer, reserved_entries); + *used_entries = reserved_entries; + *dead_entries = reserved_entries; } } void -BufferTypeBase::onHold(uint32_t buffer_id, const std::atomic<ElemCount>* usedElems, const std::atomic<ElemCount>* deadElems) +BufferTypeBase::on_hold(uint32_t buffer_id, const std::atomic<EntryCount>* used_entries, const std::atomic<EntryCount>* dead_entries) { ++_holdBuffers; auto itr = std::find(_active_buffers.begin(), _active_buffers.end(), buffer_id); assert(itr != _active_buffers.end()); _active_buffers.erase(itr); - _aggr_counts.remove_buffer(usedElems, deadElems); - _holdUsedElems += *usedElems; + _aggr_counts.remove_buffer(used_entries, dead_entries); + _hold_used_entries += *used_entries; } void -BufferTypeBase::onFree(ElemCount usedElems) +BufferTypeBase::on_free(EntryCount used_entries) { --_holdBuffers; - assert(_holdUsedElems >= usedElems); - _holdUsedElems -= usedElems; + assert(_hold_used_entries >= used_entries); + _hold_used_entries -= used_entries; } void -BufferTypeBase::resume_primary_buffer(uint32_t buffer_id, std::atomic<ElemCount>* used_elems, std::atomic<ElemCount>* dead_elems) +BufferTypeBase::resume_primary_buffer(uint32_t buffer_id, std::atomic<EntryCount>* used_entries, std::atomic<EntryCount>* dead_entries) { auto itr = std::find(_active_buffers.begin(), _active_buffers.end(), buffer_id); assert(itr != _active_buffers.end()); _active_buffers.erase(itr); _active_buffers.emplace_back(buffer_id); - _aggr_counts.remove_buffer(used_elems, dead_elems); - _aggr_counts.add_buffer(used_elems, dead_elems); + _aggr_counts.remove_buffer(used_entries, dead_entries); + _aggr_counts.add_buffer(used_entries, dead_entries); } const alloc::MemoryAllocator* @@ -115,17 +115,17 @@ BufferTypeBase::get_memory_allocator() const } void -BufferTypeBase::clampMaxArrays(uint32_t maxArrays) +BufferTypeBase::clamp_max_entries(uint32_t max_entries) { - _maxArrays = std::min(_maxArrays, maxArrays); - _minArrays = std::min(_minArrays, _maxArrays); - _numArraysForNewBuffer = std::min(_numArraysForNewBuffer, _maxArrays); + _max_entries = std::min(_max_entries, max_entries); + _min_entries = std::min(_min_entries, _max_entries); + _num_entries_for_new_buffer = std::min(_num_entries_for_new_buffer, _max_entries); } size_t -BufferTypeBase::calcArraysToAlloc(uint32_t bufferId, ElemCount elemsNeeded, bool resizing) const +BufferTypeBase::calc_entries_to_alloc(uint32_t bufferId, EntryCount free_entries_needed, bool resizing) const { - size_t reservedElems = getReservedElements(bufferId); + size_t reserved_entries = get_reserved_entries(bufferId); BufferCounts last_bc; BufferCounts bc; if (resizing) { @@ -134,56 +134,53 @@ BufferTypeBase::calcArraysToAlloc(uint32_t bufferId, ElemCount elemsNeeded, bool } } bc = _aggr_counts.all_buffers(); - assert((bc.used_elems % _arraySize) == 0); - assert((bc.dead_elems % _arraySize) == 0); - assert(bc.used_elems >= bc.dead_elems); - size_t neededArrays = (elemsNeeded + (resizing ? last_bc.used_elems : reservedElems) + _arraySize - 1) / _arraySize; - - size_t liveArrays = (bc.used_elems - bc.dead_elems) / _arraySize; - size_t growArrays = (liveArrays * _allocGrowFactor); - size_t usedArrays = last_bc.used_elems / _arraySize; - size_t wantedArrays = std::max((resizing ? usedArrays : 0u) + growArrays, - static_cast<size_t>(_minArrays)); - - size_t result = wantedArrays; - if (result < neededArrays) { - result = neededArrays; + assert(bc.used_entries >= bc.dead_entries); + size_t needed_entries = static_cast<size_t>(free_entries_needed) + (resizing ? last_bc.used_entries : reserved_entries); + size_t live_entries = (bc.used_entries - bc.dead_entries); + size_t grow_entries = (live_entries * _allocGrowFactor); + size_t used_entries = last_bc.used_entries; + size_t wanted_entries = std::max((resizing ? used_entries : 0u) + grow_entries, + static_cast<size_t>(_min_entries)); + + size_t result = wanted_entries; + if (result < needed_entries) { + result = needed_entries; } - if (result > _maxArrays) { - result = _maxArrays; + if (result > _max_entries) { + result = _max_entries; } - if (result < neededArrays) { + if (result < needed_entries) { vespalib::asciistream s; s << "BufferTypeBase::calcArraysToAlloc(" << "bufferId=" << bufferId << - ",elemsNeeeded=" << elemsNeeded << + ",free_entries_needed=" << free_entries_needed << ",resizing=" << (resizing ? "true" : "false") << ")" << - " wantedArrays=" << wantedArrays << + " wanted_entries=" << wanted_entries << ", _arraySize=" << _arraySize << - ", _maxArrays=" << _maxArrays << - ", reservedElems=" << reservedElems << - ", liveArrays=" << liveArrays << - ", growArrays=" << growArrays << - ", usedArrays=" << usedArrays << + ", _max_entries=" << _max_entries << + ", reserved_entries=" << reserved_entries << + ", live_entries=" << live_entries << + ", grow_entries=" << grow_entries << + ", used_entries=" << used_entries << ", typeid(*this).name=\"" << typeid(*this).name() << "\"" << - ", newArrays=" << result << - " < neededArrays=" << neededArrays;; + ", new_entries=" << result << + " < needed_entries=" << needed_entries; throw vespalib::OverflowException(s.c_str()); } return result; } uint32_t -BufferTypeBase::get_scaled_num_arrays_for_new_buffer() const +BufferTypeBase::get_scaled_num_entries_for_new_buffer() const { uint32_t active_buffers_count = get_active_buffers_count(); - if (active_buffers_count <= 1u || _numArraysForNewBuffer == 0u) { - return _numArraysForNewBuffer; + if (active_buffers_count <= 1u || _num_entries_for_new_buffer == 0u) { + return _num_entries_for_new_buffer; } double scale_factor = std::pow(1.0 + _allocGrowFactor, active_buffers_count - 1); - double scaled_result = _numArraysForNewBuffer * scale_factor; - if (scaled_result >= _maxArrays) { - return _maxArrays; + double scaled_result = _num_entries_for_new_buffer * scale_factor; + if (scaled_result >= _max_entries) { + return _max_entries; } return scaled_result; } @@ -194,22 +191,22 @@ BufferTypeBase::AggregatedBufferCounts::AggregatedBufferCounts() } void -BufferTypeBase::AggregatedBufferCounts::add_buffer(const std::atomic<ElemCount>* used_elems, const std::atomic<ElemCount>* dead_elems) +BufferTypeBase::AggregatedBufferCounts::add_buffer(const std::atomic<EntryCount>* used_entries, const std::atomic<EntryCount>* dead_entries) { for (const auto& elem : _counts) { - assert(elem.used_ptr != used_elems); - assert(elem.dead_ptr != dead_elems); + assert(elem.used_ptr != used_entries); + assert(elem.dead_ptr != dead_entries); } - _counts.emplace_back(used_elems, dead_elems); + _counts.emplace_back(used_entries, dead_entries); } void -BufferTypeBase::AggregatedBufferCounts::remove_buffer(const std::atomic<ElemCount>* used_elems, const std::atomic<ElemCount>* dead_elems) +BufferTypeBase::AggregatedBufferCounts::remove_buffer(const std::atomic<EntryCount>* used_entries, const std::atomic<EntryCount>* dead_entries) { auto itr = std::find_if(_counts.begin(), _counts.end(), - [=](const auto& elem){ return elem.used_ptr == used_elems; }); + [=](const auto& elem){ return elem.used_ptr == used_entries; }); assert(itr != _counts.end()); - assert(itr->dead_ptr == dead_elems); + assert(itr->dead_ptr == dead_entries); _counts.erase(itr); } @@ -219,8 +216,8 @@ BufferTypeBase::AggregatedBufferCounts::last_buffer() const BufferCounts result; assert(!_counts.empty()); const auto& last = _counts.back(); - result.used_elems += last.used_ptr->load(std::memory_order_relaxed); - result.dead_elems += last.dead_ptr->load(std::memory_order_relaxed); + result.used_entries += last.used_ptr->load(std::memory_order_relaxed); + result.dead_entries += last.dead_ptr->load(std::memory_order_relaxed); return result; } @@ -229,8 +226,8 @@ BufferTypeBase::AggregatedBufferCounts::all_buffers() const { BufferCounts result; for (const auto& elem : _counts) { - result.used_elems += elem.used_ptr->load(std::memory_order_relaxed); - result.dead_elems += elem.dead_ptr->load(std::memory_order_relaxed); + result.used_entries += elem.used_ptr->load(std::memory_order_relaxed); + result.dead_entries += elem.dead_ptr->load(std::memory_order_relaxed); } return result; } diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_type.h b/vespalib/src/vespa/vespalib/datastore/buffer_type.h index bedbb2c984e..ea52b026228 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_type.h +++ b/vespalib/src/vespa/vespalib/datastore/buffer_type.h @@ -10,7 +10,7 @@ namespace vespalib::alloc { class MemoryAllocator; } namespace vespalib::datastore { -using ElemCount = uint64_t; +using EntryCount = uint32_t; /** * Abstract class used to manage allocation and de-allocation of a specific data type in underlying memory buffers in a data store. @@ -22,7 +22,7 @@ using ElemCount = uint64_t; class BufferTypeBase { public: - using ElemCount = vespalib::datastore::ElemCount; + using EntryCount = vespalib::datastore::EntryCount; class CleanContext { private: std::atomic<size_t> &_extraUsedBytes; @@ -39,53 +39,52 @@ public: BufferTypeBase & operator=(const BufferTypeBase &rhs) = delete; BufferTypeBase(BufferTypeBase &&rhs) noexcept = default; BufferTypeBase & operator=(BufferTypeBase &&rhs) noexcept = default; - BufferTypeBase(uint32_t arraySize, uint32_t minArrays, uint32_t maxArrays) noexcept; - BufferTypeBase(uint32_t arraySize, uint32_t minArrays, uint32_t maxArrays, - uint32_t numArraysForNewBuffer, float allocGrowFactor) noexcept; + BufferTypeBase(uint32_t arraySize, uint32_t min_entries, uint32_t max_entries) noexcept; + BufferTypeBase(uint32_t arraySize, uint32_t min_entries, uint32_t max_entries, + uint32_t num_entries_for_new_buffer, float allocGrowFactor) noexcept; virtual ~BufferTypeBase(); - virtual void destroyElements(void *buffer, ElemCount numElems) = 0; - virtual void fallbackCopy(void *newBuffer, const void *oldBuffer, ElemCount numElems) = 0; + virtual void destroy_entries(void *buffer, EntryCount num_entries) = 0; + virtual void fallback_copy(void *newBuffer, const void *oldBuffer, EntryCount num_entries) = 0; /** - * Return number of reserved elements at start of buffer, to avoid - * invalid reference and handle data at negative offset (alignment - * hacks) as used by dense tensor store. + * Return number of reserved entries at start of buffer, to avoid + * invalid reference. */ - virtual ElemCount getReservedElements(uint32_t bufferId) const; + virtual EntryCount get_reserved_entries(uint32_t bufferId) const; /** * Initialize reserved elements at start of buffer. */ - virtual void initializeReservedElements(void *buffer, ElemCount reservedElements) = 0; - virtual size_t elementSize() const = 0; - virtual void cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) = 0; + virtual void initialize_reserved_entries(void *buffer, EntryCount reserved_entries) = 0; + virtual size_t entry_size() const = 0; // Size of entry measured in bytes + virtual void clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) = 0; size_t getArraySize() const { return _arraySize; } - virtual void onActive(uint32_t bufferId, std::atomic<ElemCount>* usedElems, std::atomic<ElemCount>* deadElems, void* buffer); - void onHold(uint32_t buffer_id, const std::atomic<ElemCount>* usedElems, const std::atomic<ElemCount>* deadElems); - virtual void onFree(ElemCount usedElems); - void resume_primary_buffer(uint32_t buffer_id, std::atomic<ElemCount>* used_elems, std::atomic<ElemCount>* dead_elems); + virtual void on_active(uint32_t bufferId, std::atomic<EntryCount>* used_entries, std::atomic<EntryCount>* dead_entries, void* buffer); + void on_hold(uint32_t buffer_id, const std::atomic<EntryCount>* used_entries, const std::atomic<EntryCount>* dead_entries); + virtual void on_free(EntryCount used_entries); + void resume_primary_buffer(uint32_t buffer_id, std::atomic<EntryCount>* used_entries, std::atomic<EntryCount>* dead_entries); virtual const alloc::MemoryAllocator* get_memory_allocator() const; /** - * Calculate number of arrays to allocate for new buffer given how many elements are needed. + * Calculate number of entries to allocate for new buffer given how many free entries are needed. */ - virtual size_t calcArraysToAlloc(uint32_t bufferId, ElemCount elementsNeeded, bool resizing) const; + virtual size_t calc_entries_to_alloc(uint32_t bufferId, EntryCount free_entries_needed, bool resizing) const; - void clampMaxArrays(uint32_t maxArrays); + void clamp_max_entries(uint32_t max_entries); uint32_t get_active_buffers_count() const { return _active_buffers.size(); } const std::vector<uint32_t>& get_active_buffers() const noexcept { return _active_buffers; } - size_t getMaxArrays() const { return _maxArrays; } - uint32_t get_scaled_num_arrays_for_new_buffer() const; - uint32_t get_num_arrays_for_new_buffer() const noexcept { return _numArraysForNewBuffer; } + size_t get_max_entries() const { return _max_entries; } + uint32_t get_scaled_num_entries_for_new_buffer() const; + uint32_t get_num_entries_for_new_buffer() const noexcept { return _num_entries_for_new_buffer; } protected: struct BufferCounts { - ElemCount used_elems; - ElemCount dead_elems; - BufferCounts() : used_elems(0), dead_elems(0) {} - BufferCounts(ElemCount used_elems_in, ElemCount dead_elems_in) - : used_elems(used_elems_in), dead_elems(dead_elems_in) + EntryCount used_entries; + EntryCount dead_entries; + BufferCounts() : used_entries(0), dead_entries(0) {} + BufferCounts(EntryCount used_entries_in, EntryCount dead_entries_in) + : used_entries(used_entries_in), dead_entries(dead_entries_in) {} }; @@ -94,45 +93,48 @@ protected: */ class AggregatedBufferCounts { private: - struct Element { - const std::atomic<ElemCount>* used_ptr; - const std::atomic<ElemCount>* dead_ptr; - Element() noexcept : used_ptr(nullptr), dead_ptr(nullptr) {} - Element(const std::atomic<ElemCount>* used_ptr_in, const std::atomic<ElemCount>* dead_ptr_in) noexcept + struct ActiveBufferCounts { + const std::atomic<EntryCount>* used_ptr; + const std::atomic<EntryCount>* dead_ptr; + ActiveBufferCounts() noexcept : used_ptr(nullptr), dead_ptr(nullptr) {} + ActiveBufferCounts(const std::atomic<EntryCount>* used_ptr_in, const std::atomic<EntryCount>* dead_ptr_in) noexcept : used_ptr(used_ptr_in), dead_ptr(dead_ptr_in) {} }; - std::vector<Element> _counts; + std::vector<ActiveBufferCounts> _counts; public: AggregatedBufferCounts(); - void add_buffer(const std::atomic<ElemCount>* used_elems, const std::atomic<ElemCount>* dead_elems); - void remove_buffer(const std::atomic<ElemCount>* used_elems, const std::atomic<ElemCount>* dead_elems); + void add_buffer(const std::atomic<EntryCount>* used_entries, const std::atomic<EntryCount>* dead_entries); + void remove_buffer(const std::atomic<EntryCount>* used_entries, const std::atomic<EntryCount>* dead_entries); BufferCounts last_buffer() const; BufferCounts all_buffers() const; bool empty() const { return _counts.empty(); } }; uint32_t _arraySize; // Number of elements in an allocation unit - uint32_t _minArrays; // Minimum number of arrays to allocate in a buffer - uint32_t _maxArrays; // Maximum number of arrays to allocate in a buffer - // Number of arrays needed before allocating a new buffer instead of just resizing the first one - uint32_t _numArraysForNewBuffer; + uint32_t _min_entries; // Minimum number of entries to allocate in a buffer + uint32_t _max_entries; // Maximum number of entries to allocate in a buffer + // Number of entries needed before allocating a new buffer instead of just resizing the first one + uint32_t _num_entries_for_new_buffer; float _allocGrowFactor; uint32_t _holdBuffers; - size_t _holdUsedElems; // Number of used elements in all held buffers for this type. + size_t _hold_used_entries; // Number of used entries in all held buffers for this type. AggregatedBufferCounts _aggr_counts; std::vector<uint32_t> _active_buffers; }; /** - * Concrete class used to manage allocation and de-allocation of elements of type EntryType in data store buffers. + * Concrete class used to manage allocation and de-allocation of elements of type ElemType in data store buffers. */ -template <typename EntryType, typename EmptyType = EntryType> +template <typename ElemT, typename EmptyT = ElemT> class BufferType : public BufferTypeBase { +public: + using ElemType = ElemT; + using EmptyType = EmptyT; protected: - static const EntryType& empty_entry() noexcept; + static const ElemType& empty_entry() noexcept; public: BufferType() noexcept : BufferType(1,1,1) {} @@ -140,15 +142,15 @@ public: BufferType & operator=(const BufferType &rhs) = delete; BufferType(BufferType && rhs) noexcept = default; BufferType & operator=(BufferType && rhs) noexcept = default; - BufferType(uint32_t arraySize, uint32_t minArrays, uint32_t maxArrays) noexcept; - BufferType(uint32_t arraySize, uint32_t minArrays, uint32_t maxArrays, - uint32_t numArraysForNewBuffer, float allocGrowFactor) noexcept; + BufferType(uint32_t arraySize, uint32_t min_entries, uint32_t max_entries) noexcept; + BufferType(uint32_t arraySize, uint32_t min_entries, uint32_t max_entries, + uint32_t num_entries_for_new_buffer, float allocGrowFactor) noexcept; ~BufferType() override; - void destroyElements(void *buffer, ElemCount numElems) override; - void fallbackCopy(void *newBuffer, const void *oldBuffer, ElemCount numElems) override; - void initializeReservedElements(void *buffer, ElemCount reservedElements) override; - void cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext cleanCxt) override; - size_t elementSize() const override { return sizeof(EntryType); } + void destroy_entries(void *buffer, EntryCount num_entries) override; + void fallback_copy(void *newBuffer, const void *oldBuffer, EntryCount num_entries) override; + void initialize_reserved_entries(void *buffer, EntryCount reserved_entries) override; + void clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext cleanCxt) override; + size_t entry_size() const override { return sizeof(ElemType) * _arraySize; } }; extern template class BufferType<char>; diff --git a/vespalib/src/vespa/vespalib/datastore/buffer_type.hpp b/vespalib/src/vespa/vespalib/datastore/buffer_type.hpp index 72c8f574a70..60acca5ff39 100644 --- a/vespalib/src/vespa/vespalib/datastore/buffer_type.hpp +++ b/vespalib/src/vespa/vespalib/datastore/buffer_type.hpp @@ -6,78 +6,80 @@ namespace vespalib::datastore { -template <typename EntryType, typename EmptyType> -BufferType<EntryType, EmptyType>::BufferType(uint32_t arraySize, uint32_t minArrays, uint32_t maxArrays) noexcept - : BufferTypeBase(arraySize, minArrays, maxArrays) +template <typename ElemT, typename EmptyT> +BufferType<ElemT, EmptyT>::BufferType(uint32_t arraySize, uint32_t min_entries, uint32_t max_entries) noexcept + : BufferTypeBase(arraySize, min_entries, max_entries) { } -template <typename EntryType, typename EmptyType> -BufferType<EntryType, EmptyType>::BufferType(uint32_t arraySize, uint32_t minArrays, uint32_t maxArrays, - uint32_t numArraysForNewBuffer, float allocGrowFactor) noexcept - : BufferTypeBase(arraySize, minArrays, maxArrays, numArraysForNewBuffer, allocGrowFactor) +template <typename ElemT, typename EmptyT> +BufferType<ElemT, EmptyT>::BufferType(uint32_t arraySize, uint32_t min_entries, uint32_t max_entries, + uint32_t num_entries_for_new_buffer, float allocGrowFactor) noexcept + : BufferTypeBase(arraySize, min_entries, max_entries, num_entries_for_new_buffer, allocGrowFactor) { } -template <typename EntryType, typename EmptyType> -BufferType<EntryType, EmptyType>::~BufferType() = default; +template <typename ElemT, typename EmptyT> +BufferType<ElemT, EmptyT>::~BufferType() = default; -template <typename EntryType, typename EmptyType> +template <typename ElemT, typename EmptyT> void -BufferType<EntryType, EmptyType>::destroyElements(void *buffer, ElemCount numElems) +BufferType<ElemT, EmptyT>::destroy_entries(void *buffer, EntryCount num_entries) { - EntryType *e = static_cast<EntryType *>(buffer); - for (size_t j = numElems; j != 0; --j) { - e->~EntryType(); + auto num_elems = num_entries * getArraySize(); + ElemType *e = static_cast<ElemType *>(buffer); + for (size_t j = num_elems; j != 0; --j) { + e->~ElemType(); ++e; } } -template <typename EntryType, typename EmptyType> +template <typename ElemT, typename EmptyT> void -BufferType<EntryType, EmptyType>::fallbackCopy(void *newBuffer, - const void *oldBuffer, - ElemCount numElems) +BufferType<ElemT, EmptyT>::fallback_copy(void *newBuffer, const void *oldBuffer, EntryCount num_entries) { - EntryType *d = static_cast<EntryType *>(newBuffer); - const EntryType *s = static_cast<const EntryType *>(oldBuffer); - for (size_t j = numElems; j != 0; --j) { - new (static_cast<void *>(d)) EntryType(*s); + auto num_elems = num_entries * getArraySize(); + ElemType *d = static_cast<ElemType *>(newBuffer); + const ElemType *s = static_cast<const ElemType *>(oldBuffer); + for (size_t j = num_elems; j != 0; --j) { + new (static_cast<void *>(d)) ElemType(*s); ++s; ++d; } } -template <typename EntryType, typename EmptyType> +template <typename ElemT, typename EmptyT> void -BufferType<EntryType, EmptyType>::initializeReservedElements(void *buffer, ElemCount reservedElems) +BufferType<ElemT, EmptyT>::initialize_reserved_entries(void *buffer, EntryCount reserved_entries) { - EntryType *e = static_cast<EntryType *>(buffer); + auto reserved_elems = reserved_entries * getArraySize(); + ElemType *e = static_cast<ElemType *>(buffer); const auto& empty = empty_entry(); - for (size_t j = reservedElems; j != 0; --j) { - new (static_cast<void *>(e)) EntryType(empty); + for (size_t j = reserved_elems; j != 0; --j) { + new (static_cast<void *>(e)) ElemType(empty); ++e; } } -template <typename EntryType, typename EmptyType> +template <typename ElemT, typename EmptyT> void -BufferType<EntryType, EmptyType>::cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext) +BufferType<ElemT, EmptyT>::clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext) { - EntryType *e = static_cast<EntryType *>(buffer) + offset; + auto num_elems = num_entries * getArraySize(); + ElemType *e = static_cast<ElemType *>(buffer) + offset * getArraySize(); const auto& empty = empty_entry(); - for (size_t j = numElems; j != 0; --j) { + for (size_t j = num_elems; j != 0; --j) { *e = empty; ++e; } } -template <typename EntryType, typename EmptyType> -const EntryType& -BufferType<EntryType, EmptyType>::empty_entry() noexcept +template <typename ElemT, typename EmptyT> +const ElemT& +BufferType<ElemT, EmptyT>::empty_entry() noexcept { - // It's possible for EntryType to wrap e.g. an Alloc instance, which has a transitive + // It's possible for ElemType to wrap e.g. an Alloc instance, which has a transitive // dependency on globally constructed allocator object(s). To avoid issues with global // construction order, initialize the sentinel on the first access. - static EntryType empty = EmptyType(); + static ElemType empty = EmptyType(); return empty; } diff --git a/vespalib/src/vespa/vespalib/datastore/bufferstate.cpp b/vespalib/src/vespa/vespalib/datastore/bufferstate.cpp index 47fba1ef697..f312596d6f7 100644 --- a/vespalib/src/vespa/vespalib/datastore/bufferstate.cpp +++ b/vespalib/src/vespa/vespalib/datastore/bufferstate.cpp @@ -12,13 +12,13 @@ namespace vespalib::datastore { BufferState::BufferState() : _stats(), - _free_list(_stats.dead_elems_ref()), + _free_list(_stats.dead_entries_ref()), _typeHandler(nullptr), _buffer(Alloc::alloc(0, MemoryAllocator::HUGEPAGE_SIZE)), _arraySize(0), _typeId(0), _state(State::FREE), - _disableElemHoldList(false), + _disable_entry_hold_list(false), _compacting(false) { } @@ -28,15 +28,15 @@ BufferState::~BufferState() assert(getState() == State::FREE); assert(!_free_list.enabled()); assert(_free_list.empty()); - assert(_stats.hold_elems() == 0); + assert(_stats.hold_entries() == 0); } namespace { struct AllocResult { - size_t elements; + size_t entries; size_t bytes; - AllocResult(size_t elements_, size_t bytes_) : elements(elements_), bytes(bytes_) {} + AllocResult(size_t entries_, size_t bytes_) : entries(entries_), bytes(bytes_) {} }; size_t @@ -57,30 +57,30 @@ roundUpToMatchAllocator(size_t sz) } AllocResult -calcAllocation(uint32_t bufferId, - BufferTypeBase &typeHandler, - size_t elementsNeeded, - bool resizing) +calc_allocation(uint32_t bufferId, + BufferTypeBase &typeHandler, + size_t free_entries_needed, + bool resizing) { - size_t allocArrays = typeHandler.calcArraysToAlloc(bufferId, elementsNeeded, resizing); - size_t allocElements = allocArrays * typeHandler.getArraySize(); - size_t allocBytes = roundUpToMatchAllocator(allocElements * typeHandler.elementSize()); - size_t maxAllocBytes = typeHandler.getMaxArrays() * typeHandler.getArraySize() * typeHandler.elementSize(); + size_t alloc_entries = typeHandler.calc_entries_to_alloc(bufferId, free_entries_needed, resizing); + size_t entry_size = typeHandler.entry_size(); + size_t allocBytes = roundUpToMatchAllocator(alloc_entries * entry_size); + size_t maxAllocBytes = typeHandler.get_max_entries() * entry_size; if (allocBytes > maxAllocBytes) { // Ensure that allocated bytes does not exceed the maximum handled by this type. allocBytes = maxAllocBytes; } - size_t adjustedAllocElements = (allocBytes / typeHandler.elementSize()); - return AllocResult(adjustedAllocElements, allocBytes); + size_t adjusted_alloc_entries = allocBytes / entry_size; + return AllocResult(adjusted_alloc_entries, allocBytes); } } void -BufferState::onActive(uint32_t bufferId, uint32_t typeId, - BufferTypeBase *typeHandler, - size_t elementsNeeded, - std::atomic<void*>& buffer) +BufferState::on_active(uint32_t bufferId, uint32_t typeId, + BufferTypeBase *typeHandler, + size_t free_entries_needed, + std::atomic<void*>& buffer) { assert(buffer.load(std::memory_order_relaxed) == nullptr); assert(_buffer.get() == nullptr); @@ -88,30 +88,29 @@ BufferState::onActive(uint32_t bufferId, uint32_t typeId, assert(_typeHandler == nullptr); assert(capacity() == 0); assert(size() == 0); - assert(_stats.dead_elems() == 0u); - assert(_stats.hold_elems() == 0); + assert(_stats.dead_entries() == 0u); + assert(_stats.hold_entries() == 0); assert(_stats.extra_used_bytes() == 0); assert(_stats.extra_hold_bytes() == 0); assert(_free_list.empty()); - size_t reservedElements = typeHandler->getReservedElements(bufferId); - (void) reservedElements; - AllocResult alloc = calcAllocation(bufferId, *typeHandler, elementsNeeded, false); - assert(alloc.elements >= reservedElements + elementsNeeded); + size_t reserved_entries = typeHandler->get_reserved_entries(bufferId); + (void) reserved_entries; + AllocResult alloc = calc_allocation(bufferId, *typeHandler, free_entries_needed, false); + assert(alloc.entries >= reserved_entries + free_entries_needed); auto allocator = typeHandler->get_memory_allocator(); _buffer = (allocator != nullptr) ? Alloc::alloc_with_allocator(allocator) : Alloc::alloc(0, MemoryAllocator::HUGEPAGE_SIZE); _buffer.create(alloc.bytes).swap(_buffer); - assert(_buffer.get() != nullptr || alloc.elements == 0u); + assert(_buffer.get() != nullptr || alloc.entries == 0u); buffer.store(_buffer.get(), std::memory_order_release); - _stats.set_alloc_elems(alloc.elements); + _stats.set_alloc_entries(alloc.entries); _typeHandler.store(typeHandler, std::memory_order_release); assert(typeId <= std::numeric_limits<uint16_t>::max()); _typeId = typeId; _arraySize = typeHandler->getArraySize(); - _free_list.set_array_size(_arraySize); _state.store(State::ACTIVE, std::memory_order_release); - typeHandler->onActive(bufferId, &_stats.used_elems_ref(), &_stats.dead_elems_ref(), - buffer.load(std::memory_order::relaxed)); + typeHandler->on_active(bufferId, &_stats.used_entries_ref(), &_stats.dead_entries_ref(), + buffer.load(std::memory_order::relaxed)); } void @@ -121,11 +120,11 @@ BufferState::onHold(uint32_t buffer_id) assert(getTypeHandler() != nullptr); _state.store(State::HOLD, std::memory_order_release); _compacting = false; - assert(_stats.dead_elems() <= size()); - assert(_stats.hold_elems() <= (size() - _stats.dead_elems())); - _stats.set_dead_elems(0); - _stats.set_hold_elems(size()); - getTypeHandler()->onHold(buffer_id, &_stats.used_elems_ref(), &_stats.dead_elems_ref()); + assert(_stats.dead_entries() <= size()); + assert(_stats.hold_entries() <= (size() - _stats.dead_entries())); + _stats.set_dead_entries(0); + _stats.set_hold_entries(size()); + getTypeHandler()->on_hold(buffer_id, &_stats.used_entries_ref(), &_stats.dead_entries_ref()); _free_list.disable(); } @@ -135,20 +134,19 @@ BufferState::onFree(std::atomic<void*>& buffer) assert(buffer.load(std::memory_order_relaxed) == _buffer.get()); assert(getState() == State::HOLD); assert(_typeHandler != nullptr); - assert(_stats.dead_elems() <= size()); - assert(_stats.hold_elems() == (size() - _stats.dead_elems())); - getTypeHandler()->destroyElements(buffer, size()); + assert(_stats.dead_entries() <= size()); + assert(_stats.hold_entries() == (size() - _stats.dead_entries())); + getTypeHandler()->destroy_entries(buffer, size()); Alloc::alloc().swap(_buffer); - getTypeHandler()->onFree(size()); + getTypeHandler()->on_free(size()); buffer.store(nullptr, std::memory_order_release); _stats.clear(); _state.store(State::FREE, std::memory_order_release); _typeHandler = nullptr; _arraySize = 0; - _free_list.set_array_size(_arraySize); assert(!_free_list.enabled()); assert(_free_list.empty()); - _disableElemHoldList = false; + _disable_entry_hold_list = false; } @@ -171,67 +169,67 @@ BufferState::dropBuffer(uint32_t buffer_id, std::atomic<void*>& buffer) } void -BufferState::disable_elem_hold_list() +BufferState::disable_entry_hold_list() { - _disableElemHoldList = true; + _disable_entry_hold_list = true; } bool -BufferState::hold_elems(size_t num_elems, size_t extra_bytes) +BufferState::hold_entries(size_t num_entries, size_t extra_bytes) { assert(isActive()); - if (_disableElemHoldList) { + if (_disable_entry_hold_list) { // The elements are directly marked as dead as they are not put on hold. - _stats.inc_dead_elems(num_elems); + _stats.inc_dead_entries(num_entries); return true; } - _stats.inc_hold_elems(num_elems); + _stats.inc_hold_entries(num_entries); _stats.inc_extra_hold_bytes(extra_bytes); return false; } void -BufferState::free_elems(EntryRef ref, size_t num_elems, size_t ref_offset) +BufferState::free_entries(EntryRef ref, size_t num_entries, size_t ref_offset) { if (isActive()) { - if (_free_list.enabled() && (num_elems == getArraySize())) { + if (_free_list.enabled() && (num_entries == 1)) { _free_list.push_entry(ref); } } else { assert(isOnHold()); } - _stats.inc_dead_elems(num_elems); - _stats.dec_hold_elems(num_elems); - getTypeHandler()->cleanHold(_buffer.get(), (ref_offset * _arraySize), num_elems, - BufferTypeBase::CleanContext(_stats.extra_used_bytes_ref(), - _stats.extra_hold_bytes_ref())); + _stats.inc_dead_entries(num_entries); + _stats.dec_hold_entries(num_entries); + getTypeHandler()->clean_hold(_buffer.get(), ref_offset, num_entries, + BufferTypeBase::CleanContext(_stats.extra_used_bytes_ref(), + _stats.extra_hold_bytes_ref())); } void -BufferState::fallbackResize(uint32_t bufferId, - size_t elementsNeeded, +BufferState::fallback_resize(uint32_t bufferId, + size_t free_entries_needed, std::atomic<void*>& buffer, Alloc &holdBuffer) { assert(getState() == State::ACTIVE); assert(_typeHandler != nullptr); assert(holdBuffer.get() == nullptr); - AllocResult alloc = calcAllocation(bufferId, *_typeHandler, elementsNeeded, true); - assert(alloc.elements >= size() + elementsNeeded); - assert(alloc.elements > capacity()); + AllocResult alloc = calc_allocation(bufferId, *_typeHandler, free_entries_needed, true); + assert(alloc.entries >= size() + free_entries_needed); + assert(alloc.entries > capacity()); Alloc newBuffer = _buffer.create(alloc.bytes); - getTypeHandler()->fallbackCopy(newBuffer.get(), buffer.load(std::memory_order_relaxed), size()); + getTypeHandler()->fallback_copy(newBuffer.get(), buffer.load(std::memory_order_relaxed), size()); holdBuffer.swap(_buffer); std::atomic_thread_fence(std::memory_order_release); _buffer = std::move(newBuffer); buffer.store(_buffer.get(), std::memory_order_release); - _stats.set_alloc_elems(alloc.elements); + _stats.set_alloc_entries(alloc.entries); } void BufferState::resume_primary_buffer(uint32_t buffer_id) { - getTypeHandler()->resume_primary_buffer(buffer_id, &_stats.used_elems_ref(), &_stats.dead_elems_ref()); + getTypeHandler()->resume_primary_buffer(buffer_id, &_stats.used_entries_ref(), &_stats.dead_entries_ref()); } } diff --git a/vespalib/src/vespa/vespalib/datastore/bufferstate.h b/vespalib/src/vespa/vespalib/datastore/bufferstate.h index 5b98099ed69..f714f8e24d5 100644 --- a/vespalib/src/vespa/vespalib/datastore/bufferstate.h +++ b/vespalib/src/vespa/vespalib/datastore/bufferstate.h @@ -23,8 +23,8 @@ namespace vespalib::datastore { * It is kept in this state until all reader threads are no longer accessing the buffer. * Finally, it transitions back to FREE via onFree() and memory is de-allocated. * - * This class also supports use of free lists, where previously allocated elements in the buffer can be re-used. - * First the element is put on hold, then on the free list (counted as dead) to be re-used. + * This class also supports use of free lists, where previously allocated entries in the buffer can be re-used. + * First the entry is put on hold, then on the free list (counted as dead) to be re-used. */ class BufferState { @@ -45,7 +45,7 @@ private: uint32_t _arraySize; uint16_t _typeId; std::atomic<State> _state; - bool _disableElemHoldList : 1; + bool _disable_entry_hold_list : 1; bool _compacting : 1; public: @@ -62,14 +62,14 @@ public: /** * Transition from FREE to ACTIVE state. * - * @param bufferId Id of buffer to be active. - * @param typeId Registered data type id for buffer. - * @param typeHandler Type handler for registered data type. - * @param elementsNeeded Number of elements needed to be free in the memory allocated. - * @param buffer Start of allocated buffer return value. + * @param bufferId Id of buffer to be active. + * @param typeId Registered data type id for buffer. + * @param typeHandler Type handler for registered data type. + * @param free_entries_needed Number of entries needed to be free in the memory allocated. + * @param buffer Start of allocated buffer return value. */ - void onActive(uint32_t bufferId, uint32_t typeId, BufferTypeBase *typeHandler, - size_t elementsNeeded, std::atomic<void*>& buffer); + void on_active(uint32_t bufferId, uint32_t typeId, BufferTypeBase *typeHandler, + size_t free_entries_needed, std::atomic<void*>& buffer); /** * Transition from ACTIVE to HOLD state. @@ -82,24 +82,24 @@ public: void onFree(std::atomic<void*>& buffer); /** - * Disable hold of elements, just mark elements as dead without cleanup. + * Disable hold of entries, just mark entries as dead without cleanup. * Typically used when tearing down data structure in a controlled manner. */ - void disable_elem_hold_list(); + void disable_entry_hold_list(); /** - * Update stats to reflect that the given elements are put on hold. - * Returns true if element hold list is disabled for this buffer. + * Update stats to reflect that the given entries are put on hold. + * Returns true if entry hold list is disabled for this buffer. */ - bool hold_elems(size_t num_elems, size_t extra_bytes); + bool hold_entries(size_t num_entries, size_t extra_bytes); /** - * Free the given elements and update stats accordingly. + * Free the given entries and update stats accordingly. * * The given entry ref is put on the free list (if enabled). - * Hold cleaning of elements is executed on the buffer type. + * Hold cleaning of entries is executed on the buffer type. */ - void free_elems(EntryRef ref, size_t num_elems, size_t ref_offset); + void free_entries(EntryRef ref, size_t num_entries, size_t ref_offset); BufferStats& stats() { return _stats; } const BufferStats& stats() const { return _stats; } @@ -115,8 +115,7 @@ public: uint32_t getArraySize() const { return _arraySize; } bool getCompacting() const { return _compacting; } void setCompacting() { _compacting = true; } - uint32_t get_used_arrays() const noexcept { return size() / _arraySize; } - void fallbackResize(uint32_t bufferId, size_t elementsNeeded, std::atomic<void*>& buffer, Alloc &holdBuffer); + void fallback_resize(uint32_t bufferId, size_t free_entries_needed, std::atomic<void*>& buffer, Alloc &holdBuffer); bool isActive(uint32_t typeId) const { return (isActive() && (_typeId == typeId)); diff --git a/vespalib/src/vespa/vespalib/datastore/datastore.h b/vespalib/src/vespa/vespalib/datastore/datastore.h index 01b81d0fa58..f81348ce287 100644 --- a/vespalib/src/vespa/vespalib/datastore/datastore.h +++ b/vespalib/src/vespa/vespalib/datastore/datastore.h @@ -27,7 +27,7 @@ template <typename RefT = EntryRefT<22> > class DataStoreT : public DataStoreBase { private: - void free_elem_internal(EntryRef ref, size_t numElems); + void free_entry_internal(EntryRef ref, size_t num_entries); public: using RefType = RefT; @@ -38,12 +38,12 @@ public: ~DataStoreT() override; /** - * Hold element(s). + * Hold entries. */ - void holdElem(EntryRef ref, size_t numElems) { - holdElem(ref, numElems, 0); - } - void holdElem(EntryRef ref, size_t numElems, size_t extraBytes); + void hold_entry(EntryRef ref) { hold_entries(ref, 1, 0); } + void hold_entry(EntryRef ref, size_t extra_bytes) { hold_entries(ref, 1, extra_bytes); } + void hold_entries(EntryRef ref, size_t num_entries) { hold_entries(ref, num_entries, 0); } + void hold_entries(EntryRef ref, size_t num_entries, size_t extraBytes); void reclaim_entry_refs(generation_t oldest_used_gen) override; @@ -75,7 +75,7 @@ class DataStore : public DataStoreT<RefT> { protected: using ParentType = DataStoreT<RefT>; - using ParentType::ensureBufferCapacity; + using ParentType::ensure_buffer_capacity; using ParentType::getEntry; using ParentType::dropBuffers; using ParentType::init_primary_buffers; diff --git a/vespalib/src/vespa/vespalib/datastore/datastore.hpp b/vespalib/src/vespa/vespalib/datastore/datastore.hpp index bfb63954875..b21a5954eee 100644 --- a/vespalib/src/vespa/vespalib/datastore/datastore.hpp +++ b/vespalib/src/vespa/vespalib/datastore/datastore.hpp @@ -22,21 +22,21 @@ DataStoreT<RefT>::~DataStoreT() = default; template <typename RefT> void -DataStoreT<RefT>::free_elem_internal(EntryRef ref, size_t numElems) +DataStoreT<RefT>::free_entry_internal(EntryRef ref, size_t num_entries) { RefType intRef(ref); BufferState &state = getBufferState(intRef.bufferId()); - state.free_elems(ref, numElems, intRef.offset()); + state.free_entries(ref, num_entries, intRef.offset()); } template <typename RefT> void -DataStoreT<RefT>::holdElem(EntryRef ref, size_t numElems, size_t extraBytes) +DataStoreT<RefT>::hold_entries(EntryRef ref, size_t num_entries, size_t extraBytes) { RefType intRef(ref); BufferState &state = getBufferState(intRef.bufferId()); - if (!state.hold_elems(numElems, extraBytes)) { - _entry_ref_hold_list.insert({ref, numElems}); + if (!state.hold_entries(num_entries, extraBytes)) { + _entry_ref_hold_list.insert({ref, num_entries}); } } @@ -45,7 +45,7 @@ void DataStoreT<RefT>::reclaim_entry_refs(generation_t oldest_used_gen) { _entry_ref_hold_list.reclaim(oldest_used_gen, [this](const auto& elem) { - free_elem_internal(elem.ref, elem.num_elems); + free_entry_internal(elem.ref, elem.num_entries); }); } @@ -54,7 +54,7 @@ void DataStoreT<RefT>::reclaim_all_entry_refs() { _entry_ref_hold_list.reclaim_all([this](const auto& elem) { - free_elem_internal(elem.ref, elem.num_elems); + free_entry_internal(elem.ref, elem.num_entries); }); } diff --git a/vespalib/src/vespa/vespalib/datastore/datastorebase.cpp b/vespalib/src/vespa/vespalib/datastore/datastorebase.cpp index a40aa713bca..75ffe855a32 100644 --- a/vespalib/src/vespa/vespalib/datastore/datastorebase.cpp +++ b/vespalib/src/vespa/vespalib/datastore/datastorebase.cpp @@ -40,18 +40,18 @@ constexpr size_t TOO_DEAD_SLACK = 0x4000u; bool primary_buffer_too_dead(const BufferState &state) { - size_t deadElems = state.stats().dead_elems(); - size_t deadBytes = deadElems * state.getArraySize(); - return ((deadBytes >= TOO_DEAD_SLACK) && (deadElems * 2 >= state.size())); + size_t dead_entries = state.stats().dead_entries(); + size_t deadBytes = dead_entries * state.getTypeHandler()->entry_size(); + return ((deadBytes >= TOO_DEAD_SLACK) && (dead_entries * 2 >= state.size())); } } -DataStoreBase::FallbackHold::FallbackHold(size_t bytesSize, BufferState::Alloc &&buffer, size_t usedElems, +DataStoreBase::FallbackHold::FallbackHold(size_t bytesSize, BufferState::Alloc &&buffer, size_t used_entries, BufferTypeBase *typeHandler, uint32_t typeId) : GenerationHeldBase(bytesSize), _buffer(std::move(buffer)), - _usedElems(usedElems), + _used_entries(used_entries), _typeHandler(typeHandler), _typeId(typeId) { @@ -59,7 +59,7 @@ DataStoreBase::FallbackHold::FallbackHold(size_t bytesSize, BufferState::Alloc & DataStoreBase::FallbackHold::~FallbackHold() { - _typeHandler->destroyElements(_buffer.get(), _usedElems); + _typeHandler->destroy_entries(_buffer.get(), _used_entries); } class DataStoreBase::BufferHold : public GenerationHeldBase { @@ -80,7 +80,7 @@ public: } }; -DataStoreBase::DataStoreBase(uint32_t numBuffers, uint32_t offset_bits, size_t maxArrays) +DataStoreBase::DataStoreBase(uint32_t numBuffers, uint32_t offset_bits, size_t max_entries) : _entry_ref_hold_list(), _buffers(numBuffers), _primary_buffer_ids(), @@ -89,12 +89,12 @@ DataStoreBase::DataStoreBase(uint32_t numBuffers, uint32_t offset_bits, size_t m _free_lists(), _compaction_count(0u), _genHolder(), - _maxArrays(maxArrays), + _max_entries(max_entries), _bufferIdLimit(0u), _hold_buffer_count(0u), _offset_bits(offset_bits), _freeListsEnabled(false), - _disableElemHoldList(false), + _disable_entry_hold_list(false), _initializing(false) { } @@ -105,19 +105,19 @@ DataStoreBase::~DataStoreBase() } void -DataStoreBase::switch_primary_buffer(uint32_t typeId, size_t elemsNeeded) +DataStoreBase::switch_primary_buffer(uint32_t typeId, size_t entries_needed) { size_t buffer_id = getFirstFreeBufferId(); if (buffer_id >= getMaxNumBuffers()) { LOG_ABORT(vespalib::make_string("switch_primary_buffer(%u, %zu): did not find a free buffer", - typeId, elemsNeeded).c_str()); + typeId, entries_needed).c_str()); } - onActive(buffer_id, typeId, elemsNeeded); + on_active(buffer_id, typeId, entries_needed); _primary_buffer_ids[typeId] = buffer_id; } bool -DataStoreBase::consider_grow_active_buffer(uint32_t type_id, size_t elems_needed) +DataStoreBase::consider_grow_active_buffer(uint32_t type_id, size_t entries_needed) { auto type_handler = _typeHandlers[type_id]; uint32_t buffer_id = primary_buffer_id(type_id); @@ -126,7 +126,7 @@ DataStoreBase::consider_grow_active_buffer(uint32_t type_id, size_t elems_needed if (active_buffers_count < min_active_buffers) { return false; } - if (type_handler->get_num_arrays_for_new_buffer() == 0u) { + if (type_handler->get_num_entries_for_new_buffer() == 0u) { return false; } assert(!getBufferState(buffer_id).getCompacting()); @@ -146,8 +146,7 @@ DataStoreBase::consider_grow_active_buffer(uint32_t type_id, size_t elems_needed if (checked_active_buffers < min_active_buffers) { return false; } - auto array_size = type_handler->getArraySize(); - if (elems_needed + min_used > type_handler->getMaxArrays() * array_size) { + if (entries_needed + min_used > type_handler->get_max_entries()) { return false; } if (min_buffer_id != buffer_id) { @@ -181,24 +180,22 @@ DataStoreBase::getBufferState(uint32_t buffer_id) noexcept { } void -DataStoreBase::switch_or_grow_primary_buffer(uint32_t typeId, size_t elemsNeeded) +DataStoreBase::switch_or_grow_primary_buffer(uint32_t typeId, size_t entries_needed) { auto typeHandler = _typeHandlers[typeId]; - uint32_t arraySize = typeHandler->getArraySize(); - size_t numArraysForNewBuffer = typeHandler->get_scaled_num_arrays_for_new_buffer(); - size_t numEntriesForNewBuffer = numArraysForNewBuffer * arraySize; + size_t num_entries_for_new_buffer = typeHandler->get_scaled_num_entries_for_new_buffer(); uint32_t bufferId = primary_buffer_id(typeId); - if (elemsNeeded + getBufferState(bufferId).size() >= numEntriesForNewBuffer) { - if (consider_grow_active_buffer(typeId, elemsNeeded)) { + if (entries_needed + getBufferState(bufferId).size() >= num_entries_for_new_buffer) { + if (consider_grow_active_buffer(typeId, entries_needed)) { bufferId = primary_buffer_id(typeId); - if (elemsNeeded > getBufferState(bufferId).remaining()) { - fallbackResize(bufferId, elemsNeeded); + if (entries_needed > getBufferState(bufferId).remaining()) { + fallback_resize(bufferId, entries_needed); } } else { - switch_primary_buffer(typeId, elemsNeeded); + switch_primary_buffer(typeId, entries_needed); } } else { - fallbackResize(bufferId, elemsNeeded); + fallback_resize(bufferId, entries_needed); } } @@ -209,7 +206,7 @@ DataStoreBase::init_primary_buffers() for (uint32_t typeId = 0; typeId < numTypes; ++typeId) { size_t buffer_id = getFirstFreeBufferId(); assert(buffer_id <= get_bufferid_limit_relaxed()); - onActive(buffer_id, typeId, 0u); + on_active(buffer_id, typeId, 0u); _primary_buffer_ids[typeId] = buffer_id; } } @@ -219,7 +216,7 @@ DataStoreBase::addType(BufferTypeBase *typeHandler) { uint32_t typeId = _primary_buffer_ids.size(); assert(typeId == _typeHandlers.size()); - typeHandler->clampMaxArrays(_maxArrays); + typeHandler->clamp_max_entries(_max_entries); _primary_buffer_ids.push_back(0); _typeHandlers.push_back(typeHandler); _free_lists.emplace_back(); @@ -328,12 +325,12 @@ DataStoreBase::disableFreeLists() } void -DataStoreBase::disableElemHoldList() +DataStoreBase::disable_entry_hold_list() { for_each_buffer([](BufferState & state) { - if (!state.isFree()) state.disable_elem_hold_list(); + if (!state.isFree()) state.disable_entry_hold_list(); }); - _disableElemHoldList = true; + _disable_entry_hold_list = true; } MemoryStats @@ -351,13 +348,13 @@ DataStoreBase::getMemStats() const if ((state == BufferState::State::FREE) || (typeHandler == nullptr)) { ++stats._freeBuffers; } else if (state == BufferState::State::ACTIVE) { - size_t elementSize = typeHandler->elementSize(); + size_t entry_size = typeHandler->entry_size(); ++stats._activeBuffers; - bState->stats().add_to_mem_stats(elementSize, stats); + bState->stats().add_to_mem_stats(entry_size, stats); } else if (state == BufferState::State::HOLD) { - size_t elementSize = typeHandler->elementSize(); + size_t entry_size = typeHandler->entry_size(); ++stats._holdBuffers; - bState->stats().add_to_mem_stats(elementSize, stats); + bState->stats().add_to_mem_stats(entry_size, stats); } else { LOG_ABORT("should not be reached"); } @@ -373,32 +370,30 @@ vespalib::AddressSpace DataStoreBase::getAddressSpaceUsage() const { uint32_t buffer_id_limit = get_bufferid_limit_acquire(); - size_t usedArrays = 0; - size_t deadArrays = 0; - size_t limitArrays = size_t(_maxArrays) * (getMaxNumBuffers() - buffer_id_limit); + size_t used_entries = 0; + size_t dead_entries = 0; + size_t limit_entries = size_t(_max_entries) * (getMaxNumBuffers() - buffer_id_limit); for (uint32_t bufferId = 0; bufferId < buffer_id_limit; ++bufferId) { const BufferState * bState = _buffers[bufferId].get_state_acquire(); assert(bState != nullptr); if (bState->isFree()) { - limitArrays += _maxArrays; + limit_entries += _max_entries; } else if (bState->isActive()) { - uint32_t arraySize = bState->getArraySize(); - usedArrays += bState->size() / arraySize; - deadArrays += bState->stats().dead_elems() / arraySize; - limitArrays += bState->capacity() / arraySize; + used_entries += bState->size(); + dead_entries += bState->stats().dead_entries(); + limit_entries += bState->capacity(); } else if (bState->isOnHold()) { - uint32_t arraySize = bState->getArraySize(); - usedArrays += bState->size() / arraySize; - limitArrays += bState->capacity() / arraySize; + used_entries += bState->size(); + limit_entries += bState->capacity(); } else { LOG_ABORT("should not be reached"); } } - return {usedArrays, deadArrays, limitArrays}; + return {used_entries, dead_entries, limit_entries}; } void -DataStoreBase::onActive(uint32_t bufferId, uint32_t typeId, size_t elemsNeeded) +DataStoreBase::on_active(uint32_t bufferId, uint32_t typeId, size_t entries_needed) { assert(typeId < _typeHandlers.size()); assert(bufferId <= _bufferIdLimit); @@ -407,8 +402,8 @@ DataStoreBase::onActive(uint32_t bufferId, uint32_t typeId, size_t elemsNeeded) BufferState *state = bufferMeta.get_state_relaxed(); if (state == nullptr) { BufferState & newState = _stash.create<BufferState>(); - if (_disableElemHoldList) { - newState.disable_elem_hold_list(); + if (_disable_entry_hold_list) { + newState.disable_entry_hold_list(); } if ( ! _freeListsEnabled) { newState.disable_free_list(); @@ -418,7 +413,7 @@ DataStoreBase::onActive(uint32_t bufferId, uint32_t typeId, size_t elemsNeeded) _bufferIdLimit.store(bufferId + 1, std::memory_order_release); } assert(state->isFree()); - state->onActive(bufferId, typeId, _typeHandlers[typeId], elemsNeeded, bufferMeta.get_atomic_buffer()); + state->on_active(bufferId, typeId, _typeHandlers[typeId], entries_needed, bufferMeta.get_atomic_buffer()); bufferMeta.setTypeId(typeId); bufferMeta.setArraySize(state->getArraySize()); if (_freeListsEnabled && state->isActive() && !state->getCompacting()) { @@ -436,17 +431,17 @@ DataStoreBase::finishCompact(const std::vector<uint32_t> &toHold) } void -DataStoreBase::fallbackResize(uint32_t bufferId, size_t elemsNeeded) +DataStoreBase::fallback_resize(uint32_t bufferId, size_t entries_needed) { BufferState &state = getBufferState(bufferId); BufferState::Alloc toHoldBuffer; - size_t oldUsedElems = state.size(); - size_t oldAllocElems = state.capacity(); - size_t elementSize = state.getTypeHandler()->elementSize(); - state.fallbackResize(bufferId, elemsNeeded, _buffers[bufferId].get_atomic_buffer(), toHoldBuffer); - auto hold = std::make_unique<FallbackHold>(oldAllocElems * elementSize, + size_t old_used_entries = state.size(); + size_t old_alloc_entries = state.capacity(); + size_t entry_size = state.getTypeHandler()->entry_size(); + state.fallback_resize(bufferId, entries_needed, _buffers[bufferId].get_atomic_buffer(), toHoldBuffer); + auto hold = std::make_unique<FallbackHold>(old_alloc_entries * entry_size, std::move(toHoldBuffer), - oldUsedElems, + old_used_entries, state.getTypeHandler(), state.getTypeId()); if (!_initializing) { @@ -465,7 +460,7 @@ DataStoreBase::markCompacting(uint32_t bufferId) } assert(!state.getCompacting()); state.setCompacting(); - state.disable_elem_hold_list(); + state.disable_entry_hold_list(); state.disable_free_list(); inc_compaction_count(); } @@ -492,15 +487,15 @@ DataStoreBase::start_compact_worst_buffers(CompactionSpec compaction_spec, const free_buffers++; } else if (state->isActive()) { auto typeHandler = state->getTypeHandler(); - uint32_t arraySize = typeHandler->getArraySize(); - uint32_t reservedElements = typeHandler->getReservedElements(bufferId); - size_t used_elems = state->size(); - size_t deadElems = state->stats().dead_elems() - reservedElements; + uint32_t reserved_entries = typeHandler->get_reserved_entries(bufferId); + size_t used_entries = state->size(); + size_t dead_entries = state->stats().dead_entries() - reserved_entries; + size_t entry_size = typeHandler->entry_size(); if (compaction_spec.compact_memory()) { - elem_buffers.add(bufferId, used_elems, deadElems); + elem_buffers.add(bufferId, used_entries * entry_size, dead_entries * entry_size); } if (compaction_spec.compact_address_space()) { - array_buffers.add(bufferId, used_elems / arraySize, deadElems / arraySize); + array_buffers.add(bufferId, used_entries, dead_entries); } } } diff --git a/vespalib/src/vespa/vespalib/datastore/datastorebase.h b/vespalib/src/vespa/vespalib/datastore/datastorebase.h index 9cab7a2e375..e5a38e3fd41 100644 --- a/vespalib/src/vespa/vespalib/datastore/datastorebase.h +++ b/vespalib/src/vespa/vespalib/datastore/datastorebase.h @@ -35,15 +35,16 @@ public: void init_primary_buffers(); /** - * Ensure that the primary buffer for the given type has a given number of elements free at end. + * Ensure that the primary buffer for the given type has a given number of entries free at end. * Switch to new buffer if current buffer is too full. * - * @param typeId Registered data type for buffer. - * @param elemsNeeded Number of elements needed to be free. + * @param typeId Registered data type for buffer. + * @param entries_needed Number of entries needed to be free. */ - void ensureBufferCapacity(uint32_t typeId, size_t elemsNeeded) { - if (elemsNeeded > getBufferState(primary_buffer_id(typeId)).remaining()) [[unlikely]] { - switch_or_grow_primary_buffer(typeId, elemsNeeded); + void ensure_buffer_capacity(uint32_t typeId, size_t entries_needed) { + auto &state = getBufferState(primary_buffer_id(typeId)); + if (entries_needed > state.remaining()) [[unlikely]] { + switch_or_grow_primary_buffer(typeId, entries_needed); } } @@ -58,10 +59,10 @@ public: * Switch to a new primary buffer, typically in preparation for compaction * or when the current primary buffer no longer has free space. * - * @param typeId Registered data type for buffer. - * @param elemsNeeded Number of elements needed to be free. + * @param typeId Registered data type for buffer. + * @param entries_needed Number of entries needed to be free. */ - void switch_primary_buffer(uint32_t typeId, size_t elemsNeeded); + void switch_primary_buffer(uint32_t typeId, size_t entries_needed); vespalib::MemoryUsage getMemoryUsage() const; vespalib::MemoryUsage getDynamicMemoryUsage() const; @@ -127,7 +128,7 @@ public: /** * Enable free list management. - * This only works for fixed size elements. + * This only works for fixed size entries. */ void enableFreeLists(); @@ -135,7 +136,7 @@ public: * Disable free list management. */ void disableFreeLists(); - void disableElemHoldList(); + void disable_entry_hold_list(); bool has_free_lists_enabled() const { return _freeListsEnabled; } @@ -177,25 +178,25 @@ public: bool has_held_buffers() const noexcept { return _hold_buffer_count != 0u; } /** - * Trim elem hold list, freeing elements that no longer needs to be held. + * Trim entry hold list, freeing entries that no longer needs to be held. * * @param oldest_used_gen the oldest generation that is still used. */ virtual void reclaim_entry_refs(generation_t oldest_used_gen) = 0; protected: - DataStoreBase(uint32_t numBuffers, uint32_t offset_bits, size_t maxArrays); + DataStoreBase(uint32_t numBuffers, uint32_t offset_bits, size_t max_entries); virtual ~DataStoreBase(); void* getBuffer(uint32_t bufferId) { return _buffers[bufferId].get_buffer_relaxed(); } struct EntryRefHoldElem { EntryRef ref; - size_t num_elems; + size_t num_entries; - EntryRefHoldElem(EntryRef ref_in, size_t num_elems_in) + EntryRefHoldElem(EntryRef ref_in, size_t num_entries_in) : ref(ref_in), - num_elems(num_elems_in) + num_entries(num_entries_in) {} }; @@ -215,11 +216,11 @@ private: { public: BufferState::Alloc _buffer; - size_t _usedElems; + size_t _used_entries; BufferTypeBase *_typeHandler; uint32_t _typeId; - FallbackHold(size_t bytesSize, BufferState::Alloc &&buffer, size_t usedElems, + FallbackHold(size_t bytesSize, BufferState::Alloc &&buffer, size_t used_entries, BufferTypeBase *typeHandler, uint32_t typeId); ~FallbackHold() override; @@ -227,8 +228,8 @@ private: class BufferHold; - bool consider_grow_active_buffer(uint32_t type_id, size_t elems_needed); - void switch_or_grow_primary_buffer(uint32_t typeId, size_t elemsNeeded); + bool consider_grow_active_buffer(uint32_t type_id, size_t entries_needed); + void switch_or_grow_primary_buffer(uint32_t typeId, size_t entries_needed); void markCompacting(uint32_t bufferId); /** * Hold of buffer has ended. @@ -238,14 +239,14 @@ private: /** * Switch buffer state to active for the given buffer. * - * @param bufferId Id of buffer to be active. - * @param typeId Registered data type for buffer. - * @param elemsNeeded Number of elements needed to be free. + * @param bufferId Id of buffer to be active. + * @param typeId Registered data type for buffer. + * @param entries_needed Number of entries needed to be free. */ - void onActive(uint32_t bufferId, uint32_t typeId, size_t elemsNeeded); + void on_active(uint32_t bufferId, uint32_t typeId, size_t entries_needed); void inc_hold_buffer_count(); - void fallbackResize(uint32_t bufferId, size_t elementsNeeded); + void fallback_resize(uint32_t bufferId, size_t entries_needed); uint32_t getFirstFreeBufferId(); template<typename FuncType> @@ -261,7 +262,7 @@ private: std::vector<BufferAndMeta> _buffers; // For fast mapping with known types // Provides a mapping from typeId -> primary buffer for that type. - // The primary buffer is used for allocations of new element(s) if no available slots are found in free lists. + // The primary buffer is used for allocations of new entries if no available slots are found in free lists. std::vector<uint32_t> _primary_buffer_ids; Stash _stash; @@ -269,12 +270,12 @@ private: std::vector<FreeList> _free_lists; mutable std::atomic<uint64_t> _compaction_count; vespalib::GenerationHolder _genHolder; - const uint32_t _maxArrays; + const uint32_t _max_entries; std::atomic<uint32_t> _bufferIdLimit; uint32_t _hold_buffer_count; const uint8_t _offset_bits; bool _freeListsEnabled; - bool _disableElemHoldList; + bool _disable_entry_hold_list; bool _initializing; }; diff --git a/vespalib/src/vespa/vespalib/datastore/free_list.h b/vespalib/src/vespa/vespalib/datastore/free_list.h index 20d4a6b96df..cd475af3104 100644 --- a/vespalib/src/vespa/vespalib/datastore/free_list.h +++ b/vespalib/src/vespa/vespalib/datastore/free_list.h @@ -30,7 +30,6 @@ public: bool empty() const { return _free_lists.empty(); } size_t size() const { return _free_lists.size(); } - uint32_t array_size() const { return _free_lists.back()->array_size(); } EntryRef pop_entry() { return _free_lists.back()->pop_entry(); } diff --git a/vespalib/src/vespa/vespalib/datastore/free_list_allocator.h b/vespalib/src/vespa/vespalib/datastore/free_list_allocator.h index cf899a76712..dc2d1ea3c34 100644 --- a/vespalib/src/vespa/vespalib/datastore/free_list_allocator.h +++ b/vespalib/src/vespa/vespalib/datastore/free_list_allocator.h @@ -29,7 +29,7 @@ public: HandleType alloc(Args && ... args); HandleType allocArray(ConstArrayRef array); - HandleType allocArray(size_t size); + HandleType allocArray(); }; } diff --git a/vespalib/src/vespa/vespalib/datastore/free_list_allocator.hpp b/vespalib/src/vespa/vespalib/datastore/free_list_allocator.hpp index b793e4f77a2..4e69db08a3c 100644 --- a/vespalib/src/vespa/vespalib/datastore/free_list_allocator.hpp +++ b/vespalib/src/vespa/vespalib/datastore/free_list_allocator.hpp @@ -71,8 +71,9 @@ FreeListAllocator<EntryT, RefT, ReclaimerT>::allocArray(ConstArrayRef array) if (free_list.empty()) { return ParentType::allocArray(array); } - assert(free_list.array_size() == array.size()); RefT ref = free_list.pop_entry(); + auto& state = _store.getBufferState(ref.bufferId()); + assert(state.getArraySize() == array.size()); EntryT *buf = _store.template getEntryArray<EntryT>(ref, array.size()); for (size_t i = 0; i < array.size(); ++i) { *(buf + i) = array[i]; @@ -82,14 +83,15 @@ FreeListAllocator<EntryT, RefT, ReclaimerT>::allocArray(ConstArrayRef array) template <typename EntryT, typename RefT, typename ReclaimerT> typename Allocator<EntryT, RefT>::HandleType -FreeListAllocator<EntryT, RefT, ReclaimerT>::allocArray(size_t size) +FreeListAllocator<EntryT, RefT, ReclaimerT>::allocArray() { auto& free_list = _store.getFreeList(_typeId); if (free_list.empty()) { - return ParentType::allocArray(size); + return ParentType::allocArray(); } - assert(free_list.array_size() == size); RefT ref = free_list.pop_entry(); + auto& state = _store.getBufferState(ref.bufferId()); + auto size = state.getArraySize(); EntryT *buf = _store.template getEntryArray<EntryT>(ref, size); return HandleType(ref, buf); } diff --git a/vespalib/src/vespa/vespalib/datastore/free_list_raw_allocator.h b/vespalib/src/vespa/vespalib/datastore/free_list_raw_allocator.h index 1b71c22f0ce..29684267546 100644 --- a/vespalib/src/vespa/vespalib/datastore/free_list_raw_allocator.h +++ b/vespalib/src/vespa/vespalib/datastore/free_list_raw_allocator.h @@ -27,7 +27,7 @@ private: public: FreeListRawAllocator(DataStoreBase &store, uint32_t typeId); - HandleType alloc(size_t numElems); + HandleType alloc(size_t num_entries); }; } diff --git a/vespalib/src/vespa/vespalib/datastore/free_list_raw_allocator.hpp b/vespalib/src/vespa/vespalib/datastore/free_list_raw_allocator.hpp index af832955cb7..7680cd8a9a5 100644 --- a/vespalib/src/vespa/vespalib/datastore/free_list_raw_allocator.hpp +++ b/vespalib/src/vespa/vespalib/datastore/free_list_raw_allocator.hpp @@ -14,16 +14,18 @@ FreeListRawAllocator<EntryT, RefT>::FreeListRawAllocator(DataStoreBase &store, u template <typename EntryT, typename RefT> typename FreeListRawAllocator<EntryT, RefT>::HandleType -FreeListRawAllocator<EntryT, RefT>::alloc(size_t numElems) +FreeListRawAllocator<EntryT, RefT>::alloc(size_t num_entries) { auto& free_list = _store.getFreeList(_typeId); if (free_list.empty()) { - return ParentType::alloc(numElems); + return ParentType::alloc(num_entries); } - assert(free_list.array_size() == numElems); + assert(num_entries == 1); RefT ref = free_list.pop_entry(); + auto& state = _store.getBufferState(ref.bufferId()); + auto array_size = state.getArraySize(); // We must scale the offset according to array size as it was divided when the entry ref was created. - EntryT *entry = _store.template getEntryArray<EntryT>(ref, numElems); + EntryT *entry = _store.template getEntryArray<EntryT>(ref, array_size); return HandleType(ref, entry); } diff --git a/vespalib/src/vespa/vespalib/datastore/large_array_buffer_type.h b/vespalib/src/vespa/vespalib/datastore/large_array_buffer_type.h index 600925969a3..e2718b94cd2 100644 --- a/vespalib/src/vespa/vespalib/datastore/large_array_buffer_type.h +++ b/vespalib/src/vespa/vespalib/datastore/large_array_buffer_type.h @@ -14,11 +14,11 @@ namespace vespalib::datastore { /* * Class representing buffer type for large arrays in ArrayStore */ -template <typename EntryT> -class LargeArrayBufferType : public BufferType<Array<EntryT>> +template <typename ElemT> +class LargeArrayBufferType : public BufferType<Array<ElemT>> { using AllocSpec = ArrayStoreConfig::AllocSpec; - using ArrayType = Array<EntryT>; + using ArrayType = Array<ElemT>; using ParentType = BufferType<ArrayType>; using ParentType::empty_entry; using CleanContext = typename ParentType::CleanContext; @@ -31,7 +31,7 @@ public: { } ~LargeArrayBufferType() override; - void cleanHold(void* buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) override; + void clean_hold(void* buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) override; const vespalib::alloc::MemoryAllocator* get_memory_allocator() const override; }; diff --git a/vespalib/src/vespa/vespalib/datastore/large_array_buffer_type.hpp b/vespalib/src/vespa/vespalib/datastore/large_array_buffer_type.hpp index 3042bbff73f..72a2662991b 100644 --- a/vespalib/src/vespa/vespalib/datastore/large_array_buffer_type.hpp +++ b/vespalib/src/vespa/vespalib/datastore/large_array_buffer_type.hpp @@ -7,32 +7,32 @@ namespace vespalib::datastore { -template <typename EntryT> -LargeArrayBufferType<EntryT>::LargeArrayBufferType(const AllocSpec& spec, std::shared_ptr<alloc::MemoryAllocator> memory_allocator) noexcept - : BufferType<Array<EntryT>>(1u, spec.minArraysInBuffer, spec.maxArraysInBuffer, spec.numArraysForNewBuffer, spec.allocGrowFactor), +template <typename ElemT> +LargeArrayBufferType<ElemT>::LargeArrayBufferType(const AllocSpec& spec, std::shared_ptr<alloc::MemoryAllocator> memory_allocator) noexcept + : BufferType<Array<ElemT>>(1u, spec.min_entries_in_buffer, spec.max_entries_in_buffer, spec.num_entries_for_new_buffer, spec.allocGrowFactor), _memory_allocator(std::move(memory_allocator)) { } -template <typename EntryT> -LargeArrayBufferType<EntryT>::~LargeArrayBufferType() = default; +template <typename ElemT> +LargeArrayBufferType<ElemT>::~LargeArrayBufferType() = default; -template <typename EntryT> +template <typename ElemT> void -LargeArrayBufferType<EntryT>::cleanHold(void* buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) +LargeArrayBufferType<ElemT>::clean_hold(void* buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) { ArrayType* elem = static_cast<ArrayType*>(buffer) + offset; const auto& empty = empty_entry(); - for (size_t i = 0; i < numElems; ++i) { - cleanCtx.extraBytesCleaned(sizeof(EntryT) * elem->size()); + for (size_t i = 0; i < num_entries; ++i) { + cleanCtx.extraBytesCleaned(sizeof(ElemT) * elem->size()); *elem = empty; ++elem; } } -template <typename EntryT> +template <typename ElemT> const vespalib::alloc::MemoryAllocator* -LargeArrayBufferType<EntryT>::get_memory_allocator() const +LargeArrayBufferType<ElemT>::get_memory_allocator() const { return _memory_allocator.get(); } diff --git a/vespalib/src/vespa/vespalib/datastore/memory_stats.cpp b/vespalib/src/vespa/vespalib/datastore/memory_stats.cpp index 8e060b4cfb4..5cb04796c5b 100644 --- a/vespalib/src/vespa/vespalib/datastore/memory_stats.cpp +++ b/vespalib/src/vespa/vespalib/datastore/memory_stats.cpp @@ -5,10 +5,10 @@ namespace vespalib::datastore { MemoryStats::MemoryStats() - : _allocElems(0), - _usedElems(0), - _deadElems(0), - _holdElems(0), + : _alloc_entries(0), + _used_entries(0), + _dead_entries(0), + _hold_entries(0), _allocBytes(0), _usedBytes(0), _deadBytes(0), @@ -22,10 +22,10 @@ MemoryStats::MemoryStats() MemoryStats& MemoryStats::operator+=(const MemoryStats& rhs) { - _allocElems += rhs._allocElems; - _usedElems += rhs._usedElems; - _deadElems += rhs._deadElems; - _holdElems += rhs._holdElems; + _alloc_entries += rhs._alloc_entries; + _used_entries += rhs._used_entries; + _dead_entries += rhs._dead_entries; + _hold_entries += rhs._hold_entries; _allocBytes += rhs._allocBytes; _usedBytes += rhs._usedBytes; _deadBytes += rhs._deadBytes; diff --git a/vespalib/src/vespa/vespalib/datastore/memory_stats.h b/vespalib/src/vespa/vespalib/datastore/memory_stats.h index 18d7dd77559..72a570dd625 100644 --- a/vespalib/src/vespa/vespalib/datastore/memory_stats.h +++ b/vespalib/src/vespa/vespalib/datastore/memory_stats.h @@ -13,10 +13,10 @@ namespace vespalib::datastore { class MemoryStats { public: - size_t _allocElems; - size_t _usedElems; - size_t _deadElems; - size_t _holdElems; + size_t _alloc_entries; + size_t _used_entries; + size_t _dead_entries; + size_t _hold_entries; size_t _allocBytes; size_t _usedBytes; size_t _deadBytes; diff --git a/vespalib/src/vespa/vespalib/datastore/raw_allocator.h b/vespalib/src/vespa/vespalib/datastore/raw_allocator.h index c10c8152e72..e7a59fadcf8 100644 --- a/vespalib/src/vespa/vespalib/datastore/raw_allocator.h +++ b/vespalib/src/vespa/vespalib/datastore/raw_allocator.h @@ -25,10 +25,10 @@ protected: public: RawAllocator(DataStoreBase &store, uint32_t typeId); - HandleType alloc(size_t numElems) { - return alloc(numElems, 0); + HandleType alloc(size_t num_entries) { + return alloc(num_entries, 0); } - HandleType alloc(size_t numElems, size_t extraElems); + HandleType alloc(size_t num_entries, size_t extra_entries); }; } diff --git a/vespalib/src/vespa/vespalib/datastore/raw_allocator.hpp b/vespalib/src/vespa/vespalib/datastore/raw_allocator.hpp index 04d99588218..9de361a8b19 100644 --- a/vespalib/src/vespa/vespalib/datastore/raw_allocator.hpp +++ b/vespalib/src/vespa/vespalib/datastore/raw_allocator.hpp @@ -16,19 +16,15 @@ RawAllocator<EntryT, RefT>::RawAllocator(DataStoreBase &store, uint32_t typeId) template <typename EntryT, typename RefT> typename RawAllocator<EntryT, RefT>::HandleType -RawAllocator<EntryT, RefT>::alloc(size_t numElems, size_t extraElems) +RawAllocator<EntryT, RefT>::alloc(size_t num_entries, size_t extra_entries) { - _store.ensureBufferCapacity(_typeId, numElems + extraElems); + _store.ensure_buffer_capacity(_typeId, num_entries + extra_entries); uint32_t buffer_id = _store.primary_buffer_id(_typeId); BufferState &state = _store.getBufferState(buffer_id); assert(state.isActive()); - size_t oldBufferSize = state.size(); - // Must perform scaling ourselves, according to array size - size_t arraySize = state.getArraySize(); - assert((numElems % arraySize) == 0u); - RefT ref((oldBufferSize / arraySize), buffer_id); - EntryT *buffer = _store.getEntryArray<EntryT>(ref, arraySize); - state.stats().pushed_back(numElems); + RefT ref(state.size(), buffer_id); + EntryT *buffer = _store.getEntryArray<EntryT>(ref, state.getArraySize()); + state.stats().pushed_back(num_entries); return HandleType(ref, buffer); } diff --git a/vespalib/src/vespa/vespalib/datastore/small_array_buffer_type.h b/vespalib/src/vespa/vespalib/datastore/small_array_buffer_type.h index 676a9d3790f..6bb6601839d 100644 --- a/vespalib/src/vespa/vespalib/datastore/small_array_buffer_type.h +++ b/vespalib/src/vespa/vespalib/datastore/small_array_buffer_type.h @@ -13,8 +13,8 @@ namespace vespalib::datastore { /* * Class representing buffer type for small arrays in ArrayStore */ -template <typename EntryT> -class SmallArrayBufferType : public BufferType<EntryT> +template <typename ElemT> +class SmallArrayBufferType : public BufferType<ElemT> { using AllocSpec = ArrayStoreConfig::AllocSpec; std::shared_ptr<alloc::MemoryAllocator> _memory_allocator; diff --git a/vespalib/src/vespa/vespalib/datastore/small_array_buffer_type.hpp b/vespalib/src/vespa/vespalib/datastore/small_array_buffer_type.hpp index 414804417eb..c9033936bd6 100644 --- a/vespalib/src/vespa/vespalib/datastore/small_array_buffer_type.hpp +++ b/vespalib/src/vespa/vespalib/datastore/small_array_buffer_type.hpp @@ -6,19 +6,19 @@ namespace vespalib::datastore { -template <typename EntryT> -SmallArrayBufferType<EntryT>::SmallArrayBufferType(uint32_t array_size, const AllocSpec& spec, std::shared_ptr<alloc::MemoryAllocator> memory_allocator) noexcept - : BufferType<EntryT>(array_size, spec.minArraysInBuffer, spec.maxArraysInBuffer, spec.numArraysForNewBuffer, spec.allocGrowFactor), +template <typename ElemT> +SmallArrayBufferType<ElemT>::SmallArrayBufferType(uint32_t array_size, const AllocSpec& spec, std::shared_ptr<alloc::MemoryAllocator> memory_allocator) noexcept + : BufferType<ElemT>(array_size, spec.min_entries_in_buffer, spec.max_entries_in_buffer, spec.num_entries_for_new_buffer, spec.allocGrowFactor), _memory_allocator(std::move(memory_allocator)) { } -template <typename EntryT> -SmallArrayBufferType<EntryT>::~SmallArrayBufferType() = default; +template <typename ElemT> +SmallArrayBufferType<ElemT>::~SmallArrayBufferType() = default; -template <typename EntryT> +template <typename ElemT> const vespalib::alloc::MemoryAllocator* -SmallArrayBufferType<EntryT>::get_memory_allocator() const +SmallArrayBufferType<ElemT>::get_memory_allocator() const { return _memory_allocator.get(); } diff --git a/vespalib/src/vespa/vespalib/datastore/unique_store.hpp b/vespalib/src/vespa/vespalib/datastore/unique_store.hpp index 52b0798543f..0efaf04b26e 100644 --- a/vespalib/src/vespa/vespalib/datastore/unique_store.hpp +++ b/vespalib/src/vespa/vespalib/datastore/unique_store.hpp @@ -106,7 +106,7 @@ private: _mapping.resize(data_store.get_bufferid_limit_relaxed()); for (const auto bufferId : _compacting_buffers->get_buffer_ids()) { BufferState &state = data_store.getBufferState(bufferId); - _mapping[bufferId].resize(state.get_used_arrays()); + _mapping[bufferId].resize(state.size()); } } diff --git a/vespalib/src/vespa/vespalib/datastore/unique_store_allocator.hpp b/vespalib/src/vespa/vespalib/datastore/unique_store_allocator.hpp index 8ad11b18218..49d2631e73f 100644 --- a/vespalib/src/vespa/vespalib/datastore/unique_store_allocator.hpp +++ b/vespalib/src/vespa/vespalib/datastore/unique_store_allocator.hpp @@ -43,7 +43,7 @@ template <typename EntryT, typename RefT> void UniqueStoreAllocator<EntryT, RefT>::hold(EntryRef ref) { - _store.holdElem(ref, 1); + _store.hold_entry(ref); } template <typename EntryT, typename RefT> diff --git a/vespalib/src/vespa/vespalib/datastore/unique_store_enumerator.hpp b/vespalib/src/vespa/vespalib/datastore/unique_store_enumerator.hpp index 32513d09c72..4a517521d77 100644 --- a/vespalib/src/vespa/vespalib/datastore/unique_store_enumerator.hpp +++ b/vespalib/src/vespa/vespalib/datastore/unique_store_enumerator.hpp @@ -44,7 +44,7 @@ UniqueStoreEnumerator<RefT>::allocate_enum_values(DataStoreBase & store) { _enumValues.resize(store.get_bufferid_limit_relaxed()); store.for_each_active_buffer([this](uint32_t buffer_id, const BufferState & state) { - _enumValues[buffer_id].resize(state.get_used_arrays()); + _enumValues[buffer_id].resize(state.size()); }); } diff --git a/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.cpp b/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.cpp index 1d3ba27d6bf..9f2105b7f08 100644 --- a/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.cpp +++ b/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.cpp @@ -42,28 +42,28 @@ UniqueStoreSmallStringBufferType::UniqueStoreSmallStringBufferType(uint32_t arra UniqueStoreSmallStringBufferType::~UniqueStoreSmallStringBufferType() = default; void -UniqueStoreSmallStringBufferType::destroyElements(void *, ElemCount) +UniqueStoreSmallStringBufferType::destroy_entries(void *, EntryCount) { static_assert(std::is_trivially_destructible<UniqueStoreSmallStringEntry>::value, "UniqueStoreSmallStringEntry must be trivially destructable"); } void -UniqueStoreSmallStringBufferType::fallbackCopy(void *newBuffer, const void *oldBuffer, ElemCount numElems) +UniqueStoreSmallStringBufferType::fallback_copy(void *newBuffer, const void *oldBuffer, EntryCount num_entries) { static_assert(std::is_trivially_copyable<UniqueStoreSmallStringEntry>::value, "UniqueStoreSmallStringEntry must be trivially copyable"); - if (numElems > 0) { - memcpy(newBuffer, oldBuffer, numElems); + if (num_entries > 0) { + memcpy(newBuffer, oldBuffer, num_entries * getArraySize()); } } void -UniqueStoreSmallStringBufferType::cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext) +UniqueStoreSmallStringBufferType::clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext) { - void *e = static_cast<char *>(buffer) + offset; - void *e_end = static_cast<char *>(e) + numElems; size_t array_size = getArraySize(); + void *e = static_cast<char *>(buffer) + offset * array_size; + void *e_end = static_cast<char *>(e) + num_entries * array_size; while (e < e_end) { static_cast<UniqueStoreSmallStringEntry *>(e)->clean_hold(array_size); e = static_cast<char *>(e) + array_size; @@ -86,10 +86,10 @@ UniqueStoreExternalStringBufferType::UniqueStoreExternalStringBufferType(uint32_ UniqueStoreExternalStringBufferType::~UniqueStoreExternalStringBufferType() = default; void -UniqueStoreExternalStringBufferType::cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) +UniqueStoreExternalStringBufferType::clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) { UniqueStoreEntry<std::string> *elem = static_cast<UniqueStoreEntry<std::string> *>(buffer) + offset; - for (size_t i = 0; i < numElems; ++i) { + for (size_t i = 0; i < num_entries; ++i) { cleanCtx.extraBytesCleaned(elem->value().size() + 1); std::string().swap(elem->value()); ++elem; diff --git a/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.h b/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.h index a85b73f423d..d3348950891 100644 --- a/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.h +++ b/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.h @@ -62,9 +62,9 @@ class UniqueStoreSmallStringBufferType : public BufferType<char> { public: UniqueStoreSmallStringBufferType(uint32_t array_size, uint32_t max_arrays, std::shared_ptr<vespalib::alloc::MemoryAllocator> memory_allocator); ~UniqueStoreSmallStringBufferType() override; - void destroyElements(void *, ElemCount) override; - void fallbackCopy(void *newBuffer, const void *oldBuffer, ElemCount numElems) override; - void cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext) override; + void destroy_entries(void *, EntryCount) override; + void fallback_copy(void *newBuffer, const void *oldBuffer, EntryCount numElems) override; + void clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext) override; const vespalib::alloc::MemoryAllocator* get_memory_allocator() const override; }; @@ -76,7 +76,7 @@ class UniqueStoreExternalStringBufferType : public BufferType<UniqueStoreEntry<s public: UniqueStoreExternalStringBufferType(uint32_t array_size, uint32_t max_arrays, std::shared_ptr<vespalib::alloc::MemoryAllocator> memory_allocator); ~UniqueStoreExternalStringBufferType() override; - void cleanHold(void *buffer, size_t offset, ElemCount numElems, CleanContext cleanCtx) override; + void clean_hold(void *buffer, size_t offset, EntryCount num_entries, CleanContext cleanCtx) override; const vespalib::alloc::MemoryAllocator* get_memory_allocator() const override; }; diff --git a/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.hpp b/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.hpp index 65cab4850ba..4ff8e1e1ab4 100644 --- a/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.hpp +++ b/vespalib/src/vespa/vespalib/datastore/unique_store_string_allocator.hpp @@ -42,7 +42,7 @@ UniqueStoreStringAllocator<RefT>::allocate(const char *value) uint32_t type_id = string_allocator::get_type_id(value_len); if (type_id != 0) { size_t array_size = string_allocator::array_sizes[type_id - 1]; - auto handle = _store.template freeListRawAllocator<char>(type_id).alloc(array_size); + auto handle = _store.template freeListRawAllocator<char>(type_id).alloc(1); new (static_cast<void *>(handle.data)) UniqueStoreSmallStringEntry(value, value_len, array_size); return handle.ref; } else { @@ -61,11 +61,10 @@ UniqueStoreStringAllocator<RefT>::hold(EntryRef ref) RefT iRef(ref); uint32_t type_id = _store.getTypeId(iRef.bufferId()); if (type_id != 0) { - size_t array_size = string_allocator::array_sizes[type_id - 1]; - _store.holdElem(ref, array_size); + _store.hold_entry(ref); } else { auto &value = _store.template getEntry<WrappedExternalEntryType>(iRef)->value(); - _store.holdElem(ref, 1, value.size() + 1); + _store.hold_entry(ref, value.size() + 1); } } @@ -79,7 +78,7 @@ UniqueStoreStringAllocator<RefT>::move_on_compact(EntryRef ref) static_assert(std::is_trivially_copyable<UniqueStoreSmallStringEntry>::value, "UniqueStoreSmallStringEntry must be trivially copyable"); size_t array_size = string_allocator::array_sizes[type_id - 1]; - auto handle = _store.template rawAllocator<char>(type_id).alloc(array_size); + auto handle = _store.template rawAllocator<char>(type_id).alloc(1); auto orig = _store.template getEntryArray<char>(iRef, array_size); memcpy(handle.data, orig, array_size); return handle.ref; diff --git a/vespalib/src/vespa/vespalib/util/CMakeLists.txt b/vespalib/src/vespa/vespalib/util/CMakeLists.txt index 663b3b65638..8ee3957af32 100644 --- a/vespalib/src/vespa/vespalib/util/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/util/CMakeLists.txt @@ -100,3 +100,7 @@ vespa_add_library(vespalib_vespalib_util OBJECT zstdcompressor.cpp DEPENDS ) + +# Don't convert call to jump when returning a value from a function with +# a compatible stack. +set_source_files_properties(signalhandler.cpp PROPERTIES COMPILE_OPTIONS "-fno-optimize-sibling-calls") diff --git a/vespalib/src/vespa/vespalib/util/time.cpp b/vespalib/src/vespa/vespalib/util/time.cpp index cba26f24059..dd4972b1c21 100644 --- a/vespalib/src/vespa/vespalib/util/time.cpp +++ b/vespalib/src/vespa/vespalib/util/time.cpp @@ -93,7 +93,7 @@ Timer::waitAtLeast(duration dur, bool busyWait) { } -#if (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000) || (!defined(_LIBCPP_VERSION) && defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 12) +#if (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 170000) || (!defined(_LIBCPP_VERSION) && defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 12) // Temporary workaround until libc++ supports stream operators for duration // Temporary workaround while using libstdc++ 11 diff --git a/vespalib/src/vespa/vespalib/util/time.h b/vespalib/src/vespa/vespalib/util/time.h index b893661832f..10077a0b49a 100644 --- a/vespalib/src/vespa/vespalib/util/time.h +++ b/vespalib/src/vespa/vespalib/util/time.h @@ -101,7 +101,7 @@ duration adjustTimeoutByHz(duration timeout, long hz); } -#if (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000) || (!defined(_LIBCPP_VERSION) && defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 12) +#if (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 170000) || (!defined(_LIBCPP_VERSION) && defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 12) // Temporary workaround until libc++ supports stream operators for duration // Temporary workaround while using libstdc++ 11 |