summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@oath.com>2021-09-27 23:08:47 +0200
committerGitHub <noreply@github.com>2021-09-27 23:08:47 +0200
commit8f3fb1a105ded07144f6de527266a438e48a1766 (patch)
treed2923a45682e91d80e7011c60cfb301e05acead3
parent0a858cc0f3425fc792d978b6221ef4f3c3bc6067 (diff)
parent037f756caf4cfb99bcd988174839d7bc385267b9 (diff)
Merge branch 'master' into bratseth/linguistics-components
-rw-r--r--client/go/cmd/api_key.go2
-rw-r--r--client/go/cmd/api_key_test.go12
-rw-r--r--client/go/cmd/cert_test.go8
-rw-r--r--client/go/cmd/clone.go99
-rw-r--r--client/go/cmd/clone_test.go11
-rw-r--r--client/go/cmd/command_tester.go13
-rw-r--r--client/go/cmd/config.go24
-rw-r--r--client/go/cmd/config_test.go3
-rw-r--r--client/go/cmd/curl_test.go7
-rw-r--r--client/go/cmd/deploy.go8
-rw-r--r--client/go/cmd/deploy_test.go2
-rw-r--r--client/go/cmd/document_test.go5
-rw-r--r--client/go/cmd/helpers.go61
-rw-r--r--client/go/cmd/query_test.go3
-rw-r--r--client/go/cmd/status_test.go5
-rw-r--r--client/go/vespa/deploy.go2
-rw-r--r--client/go/vespa/target.go67
-rw-r--r--client/go/vespa/target_test.go18
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java5
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java5
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java8
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/DistributableResource.java8
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java14
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java6
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingOperation.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java4
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java1
-rwxr-xr-xconfig-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/PlatformBundles.java133
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/PlatformBundles.java119
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java1
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java16
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java3
-rwxr-xr-xconfig-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java7
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java1
-rw-r--r--config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigVerification.java (renamed from config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java)2
-rw-r--r--config-proxy/src/main/sh/vespa-config-verification.sh2
-rw-r--r--config/src/apps/vespa-get-config/getconfig.cpp2
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java8
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/GenericConfig.java1
-rw-r--r--config/src/tests/configagent/configagent.cpp20
-rw-r--r--config/src/tests/failover/failover.cpp2
-rw-r--r--config/src/tests/frt/frt.cpp22
-rw-r--r--config/src/tests/misc/misc.cpp27
-rw-r--r--config/src/tests/subscriber/subscriber.cpp2
-rw-r--r--config/src/vespa/config/common/configstate.h10
-rw-r--r--config/src/vespa/config/common/configvalue.cpp12
-rw-r--r--config/src/vespa/config/common/configvalue.h8
-rw-r--r--config/src/vespa/config/common/misc.cpp23
-rw-r--r--config/src/vespa/config/common/misc.h2
-rw-r--r--config/src/vespa/config/file/filesource.cpp4
-rw-r--r--config/src/vespa/config/frt/frtconfigagent.cpp10
-rw-r--r--config/src/vespa/config/frt/frtconfigrequestfactory.cpp2
-rw-r--r--config/src/vespa/config/frt/frtconfigresponsev3.cpp8
-rw-r--r--config/src/vespa/config/frt/protocol.cpp3
-rw-r--r--config/src/vespa/config/frt/protocol.h4
-rw-r--r--config/src/vespa/config/frt/slimeconfigrequest.cpp10
-rw-r--r--config/src/vespa/config/frt/slimeconfigrequest.h4
-rw-r--r--config/src/vespa/config/frt/slimeconfigresponse.cpp4
-rw-r--r--config/src/vespa/config/print/asciiconfigreader.hpp2
-rw-r--r--config/src/vespa/config/print/fileconfigreader.hpp2
-rw-r--r--config/src/vespa/config/print/istreamconfigreader.hpp2
-rw-r--r--config/src/vespa/config/raw/rawsource.cpp2
-rw-r--r--config/src/vespa/config/retriever/configsnapshot.cpp8
-rw-r--r--config/src/vespa/config/set/configinstancesourcefactory.cpp4
-rw-r--r--config/src/vespa/config/set/configsetsource.cpp10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/GetConfigContext.java6
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/application/TenantApplications.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java7
-rwxr-xr-xconfigserver/src/main/sh/start-configserver1
-rwxr-xr-xcontainer-core/src/main/sh/find-pid28
-rw-r--r--container-disc/pom.xml19
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java3
-rwxr-xr-xcontainer-disc/src/main/sh/vespa-start-container-daemon.sh1
-rw-r--r--container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/NetworkMultiplexerHolder.java2
-rw-r--r--container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java6
-rw-r--r--container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java30
-rw-r--r--container-messagebus/src/main/java/com/yahoo/messagebus/shared/ClientSession.java2
-rw-r--r--container-messagebus/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java1
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/NoopRoleService.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/RoleService.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java4
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java)0
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java)0
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java)0
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/LastLoginInfo.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/LastLoginInfo.java)0
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java)0
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java)4
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java)0
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java)0
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/package-info.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/package-info.java)0
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDb.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainer.java60
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/TenantRoleMaintainer.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java14
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java62
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java97
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-applications.json1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-trial-capacity-cloud.json1
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java7
-rwxr-xr-xdocumentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java7
-rw-r--r--eval/CMakeLists.txt1
-rw-r--r--eval/src/tests/instruction/dense_hamming_distance/CMakeLists.txt9
-rw-r--r--eval/src/tests/instruction/dense_hamming_distance/dense_hamming_distance_test.cpp91
-rw-r--r--eval/src/vespa/eval/eval/optimize_tensor_function.cpp2
-rw-r--r--eval/src/vespa/eval/instruction/CMakeLists.txt1
-rw-r--r--eval/src/vespa/eval/instruction/dense_hamming_distance.cpp91
-rw-r--r--eval/src/vespa/eval/instruction/dense_hamming_distance.h22
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java16
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java1
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java10
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java6
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java4
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/network/NetworkMultiplexer.java35
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/network/rpc/NamedRPCService.java48
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCService.java60
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServiceAddress.java7
-rwxr-xr-xmessagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java35
-rw-r--r--messagebus/src/main/java/com/yahoo/messagebus/network/rpc/TcpRPCService.java23
-rw-r--r--messagebus/src/test/java/com/yahoo/messagebus/network/NetworkMultiplexerTest.java6
-rwxr-xr-xmessagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java4
-rw-r--r--searchcore/CMakeLists.txt1
-rw-r--r--searchcore/src/apps/vespa-redistribute-bm/vespa_redistribute_bm.cpp91
-rw-r--r--searchcore/src/tests/bmcluster/estimate_moved_docs_ratio/CMakeLists.txt11
-rw-r--r--searchcore/src/tests/bmcluster/estimate_moved_docs_ratio/estimate_moved_docs_ratio_test.cpp134
-rw-r--r--searchcore/src/tests/proton/server/disk_mem_usage_sampler/disk_mem_usage_sampler_test.cpp6
-rw-r--r--searchcore/src/vespa/searchcore/bmcluster/CMakeLists.txt2
-rw-r--r--searchcore/src/vespa/searchcore/bmcluster/bm_cluster_params.cpp2
-rw-r--r--searchcore/src/vespa/searchcore/bmcluster/bm_node_stats_reporter.cpp8
-rw-r--r--searchcore/src/vespa/searchcore/bmcluster/calculate_moved_docs_ratio.cpp142
-rw-r--r--searchcore/src/vespa/searchcore/bmcluster/calculate_moved_docs_ratio.h48
-rw-r--r--searchcore/src/vespa/searchcore/bmcluster/estimate_moved_docs_ratio.cpp114
-rw-r--r--searchcore/src/vespa/searchcore/bmcluster/estimate_moved_docs_ratio.h25
-rw-r--r--searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp12
-rw-r--r--slobrok/src/tests/rpc_mapping_monitor/rpc_mapping_monitor_test.cpp4
-rw-r--r--standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java4
-rwxr-xr-xstandalone-container/src/main/sh/standalone-container.sh1
-rw-r--r--storage/src/tests/distributor/distributor_stripe_test.cpp19
-rw-r--r--vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClient.java11
-rw-r--r--vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpRequestStrategy.java2
-rw-r--r--vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java5
-rw-r--r--vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java2
-rw-r--r--vespajlib/src/main/java/com/yahoo/slime/ArrayValue.java20
157 files changed, 1688 insertions, 795 deletions
diff --git a/client/go/cmd/api_key.go b/client/go/cmd/api_key.go
index a838f1a05c8..b3284daa993 100644
--- a/client/go/cmd/api_key.go
+++ b/client/go/cmd/api_key.go
@@ -77,6 +77,6 @@ func printPublicKey(apiKeyFile, tenant string) {
log.Printf("\nThis is your public key:\n%s", color.Green(pemPublicKey))
log.Printf("Its fingerprint is:\n%s\n", color.Cyan(fingerprint))
log.Print("\nTo use this key in Vespa Cloud click 'Add custom key' at")
- log.Printf(color.Cyan("%s/tenant/%s/keys").String(), defaultConsoleURL, tenant)
+ log.Printf(color.Cyan("%s/tenant/%s/keys").String(), getConsoleURL(), tenant)
log.Print("and paste the entire public key including the BEGIN and END lines.")
}
diff --git a/client/go/cmd/api_key_test.go b/client/go/cmd/api_key_test.go
index 2497568604f..1deb628c21e 100644
--- a/client/go/cmd/api_key_test.go
+++ b/client/go/cmd/api_key_test.go
@@ -4,20 +4,20 @@
package cmd
import (
- "strings"
+ "path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAPIKey(t *testing.T) {
- homeDir := t.TempDir()
- keyFile := homeDir + "/.vespa/t1.api-key.pem"
+ homeDir := filepath.Join(t.TempDir(), ".vespa")
+ keyFile := filepath.Join(homeDir, "t1.api-key.pem")
out, _ := execute(command{args: []string{"api-key", "-a", "t1.a1.i1"}, homeDir: homeDir}, t, nil)
- assert.True(t, strings.HasPrefix(out, "Success: API private key written to "+keyFile+"\n"))
+ assert.Contains(t, out, "Success: API private key written to "+keyFile+"\n")
out, _ = execute(command{args: []string{"api-key", "-a", "t1.a1.i1"}, homeDir: homeDir}, t, nil)
- assert.True(t, strings.HasPrefix(out, "Error: File "+keyFile+" already exists\nHint: Use -f to overwrite it\n"))
- assert.True(t, strings.Contains(out, "This is your public key"))
+ assert.Contains(t, out, "Error: File "+keyFile+" already exists\nHint: Use -f to overwrite it\n")
+ assert.Contains(t, out, "This is your public key")
}
diff --git a/client/go/cmd/cert_test.go b/client/go/cmd/cert_test.go
index d93def2fa70..cd5f88764b9 100644
--- a/client/go/cmd/cert_test.go
+++ b/client/go/cmd/cert_test.go
@@ -14,7 +14,7 @@ import (
)
func TestCert(t *testing.T) {
- homeDir := t.TempDir()
+ homeDir := filepath.Join(t.TempDir(), ".vespa")
pkgDir := mockApplicationPackage(t, false)
out, _ := execute(command{args: []string{"cert", "-a", "t1.a1.i1", pkgDir}, homeDir: homeDir}, t, nil)
@@ -23,8 +23,8 @@ func TestCert(t *testing.T) {
appDir := filepath.Join(pkgDir, "src", "main", "application")
pkgCertificate := filepath.Join(appDir, "security", "clients.pem")
- certificate := filepath.Join(homeDir, ".vespa", app.String(), "data-plane-public-cert.pem")
- privateKey := filepath.Join(homeDir, ".vespa", app.String(), "data-plane-private-key.pem")
+ certificate := filepath.Join(homeDir, app.String(), "data-plane-public-cert.pem")
+ privateKey := filepath.Join(homeDir, app.String(), "data-plane-private-key.pem")
assert.Equal(t, fmt.Sprintf("Success: Certificate written to %s\nSuccess: Certificate written to %s\nSuccess: Private key written to %s\n", pkgCertificate, certificate, privateKey), out)
@@ -33,7 +33,7 @@ func TestCert(t *testing.T) {
}
func TestCertCompressedPackage(t *testing.T) {
- homeDir := t.TempDir()
+ homeDir := filepath.Join(t.TempDir(), ".vespa")
pkgDir := mockApplicationPackage(t, true)
zipFile := filepath.Join(pkgDir, "target", "application.zip")
err := os.MkdirAll(filepath.Dir(zipFile), 0755)
diff --git a/client/go/cmd/clone.go b/client/go/cmd/clone.go
index 9503a81debf..508ad49438f 100644
--- a/client/go/cmd/clone.go
+++ b/client/go/cmd/clone.go
@@ -6,11 +6,10 @@ package cmd
import (
"archive/zip"
+ "errors"
"io"
- "io/ioutil"
"log"
"net/http"
- "net/url"
"os"
"path/filepath"
"strings"
@@ -20,22 +19,29 @@ import (
"github.com/vespa-engine/vespa/client/go/util"
)
-// Set this to test without downloading this file from github
-var existingSampleAppsZip string
+const sampleAppsCacheTTL = time.Hour * 168 // 1 week
+
var listApps bool
+var forceClone bool
func init() {
rootCmd.AddCommand(cloneCmd)
cloneCmd.Flags().BoolVarP(&listApps, "list", "l", false, "List available sample applications")
+ cloneCmd.Flags().BoolVarP(&forceClone, "force", "f", false, "Ignore cache and force downloading the latest sample application from GitHub")
}
var cloneCmd = &cobra.Command{
- // TODO: "application" and "list" subcommands?
Use: "clone sample-application-path target-directory",
Short: "Create files and directory structure for a new Vespa application from a sample application",
- Long: `Creates an application package file structure.
+ Long: `Create files and directory structure for a new Vespa application
+from a sample application.
+
+Sample applications are downloaded from
+https://github.com/vespa-engine/sample-apps.
-The application package is copied from a sample application in https://github.com/vespa-engine/sample-apps`,
+By default sample applications are cached in the user's cache directory. This
+directory can be overriden by setting the VESPA_CLI_CACHE_DIR environment
+variable.`,
Example: "$ vespa clone vespa-cloud/album-recommendation my-app",
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
@@ -60,12 +66,7 @@ The application package is copied from a sample application in https://github.co
func cloneApplication(source string, name string) {
zipFile := getSampleAppsZip()
- if zipFile == nil {
- return
- }
- if existingSampleAppsZip == "" { // Indicates we created a temp file now
- defer os.Remove(zipFile.Name())
- }
+ defer zipFile.Close()
zipReader, zipOpenError := zip.OpenReader(zipFile.Name())
if zipOpenError != nil {
@@ -101,45 +102,67 @@ func cloneApplication(source string, name string) {
}
}
+func openOutputFile() (*os.File, error) {
+ cacheDir, err := vespaCliCacheDir()
+ if err != nil {
+ return nil, err
+ }
+ cacheFile := filepath.Join(cacheDir, "sample-apps-master.zip")
+ return os.OpenFile(cacheFile, os.O_RDWR|os.O_CREATE, 0755)
+}
+
+func useCache(cacheFile *os.File) (bool, error) {
+ if forceClone {
+ return false, nil
+ }
+ stat, err := cacheFile.Stat()
+ if errors.Is(err, os.ErrNotExist) {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+ expiry := stat.ModTime().Add(sampleAppsCacheTTL)
+ return stat.Size() > 0 && time.Now().Before(expiry), nil
+}
+
func getSampleAppsZip() *os.File {
- if existingSampleAppsZip != "" {
- existing, openExistingError := os.Open(existingSampleAppsZip)
- if openExistingError != nil {
- printErr(openExistingError, "Could not open existing sample apps zip file '", color.Cyan(existingSampleAppsZip), "'")
- }
- return existing
+ f, err := openOutputFile()
+ if err != nil {
+ fatalErr(err, "Could not determine location of cache file")
+ return nil
+ }
+ useCache, err := useCache(f)
+ if err != nil {
+ fatalErr(err, "Could not determine cache status", "Try ignoring the cache with the -f flag")
+ return nil
+ }
+ if useCache {
+ log.Print(color.Yellow("Using cached sample apps ..."))
+ return f
}
- // TODO: Cache it?
log.Print(color.Yellow("Downloading sample apps ...")) // TODO: Spawn thread to indicate progress
- zipUrl, _ := url.Parse("https://github.com/vespa-engine/sample-apps/archive/refs/heads/master.zip")
- request := &http.Request{
- URL: zipUrl,
- Method: "GET",
+ request, err := http.NewRequest("GET", "https://github.com/vespa-engine/sample-apps/archive/refs/heads/master.zip", nil)
+ if err != nil {
+ fatalErr(err, "Invalid URL")
+ return nil
}
- response, reqErr := util.HttpDo(request, time.Minute*60, "GitHub")
- if reqErr != nil {
- printErr(reqErr, "Could not download sample apps from GitHub")
+ response, err := util.HttpDo(request, time.Minute*60, "GitHub")
+ if err != nil {
+ fatalErr(err, "Could not download sample apps from GitHub")
return nil
}
defer response.Body.Close()
if response.StatusCode != 200 {
- printErr(nil, "Could not download sample apps from GitHub: ", response.StatusCode)
+ fatalErr(nil, "Could not download sample apps from GitHub: ", response.StatusCode)
return nil
}
- destination, tempFileError := ioutil.TempFile("", "prefix")
- if tempFileError != nil {
- printErr(tempFileError, "Could not create a temporary file to hold sample apps")
- }
- // destination, _ := os.Create("./" + name + "/sample-apps.zip")
- // defer destination.Close()
- _, err := io.Copy(destination, response.Body)
- if err != nil {
- printErr(err, "Could not download sample apps from GitHub")
+ if _, err := io.Copy(f, response.Body); err != nil {
+ fatalErr(err, "Could not write sample apps to file: ", f.Name())
return nil
}
- return destination
+ return f
}
func copy(f *zip.File, destinationDir string, zipEntryPrefix string) error {
diff --git a/client/go/cmd/clone_test.go b/client/go/cmd/clone_test.go
index 5027c5bf972..6cf11dd4d40 100644
--- a/client/go/cmd/clone_test.go
+++ b/client/go/cmd/clone_test.go
@@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"testing"
+ "time"
"github.com/stretchr/testify/assert"
"github.com/vespa-engine/vespa/client/go/util"
@@ -18,10 +19,14 @@ func TestClone(t *testing.T) {
}
func assertCreated(sampleAppName string, app string, t *testing.T) {
- existingSampleAppsZip = "testdata/sample-apps-master.zip"
- standardOut := executeCommand(t, &mockHttpClient{}, []string{"clone", sampleAppName, app}, []string{})
+ testFile := filepath.Join("testdata", "sample-apps-master.zip")
+ now := time.Now()
+ if err := os.Chtimes(testFile, now, now); err != nil { // Ensure test file is considered new enough by cache mechanism
+ t.Fatal(err)
+ }
+ out, _ := execute(command{cacheDir: filepath.Dir(testFile), args: []string{"clone", sampleAppName, app}}, t, nil)
defer os.RemoveAll(app)
- assert.Equal(t, "Created "+app+"\n", standardOut)
+ assert.Equal(t, "Using cached sample apps ...\nCreated "+app+"\n", out)
assert.True(t, util.PathExists(filepath.Join(app, "README.md")))
assert.True(t, util.PathExists(filepath.Join(app, "src", "main", "application")))
assert.True(t, util.IsDirectory(filepath.Join(app, "src", "main", "application")))
diff --git a/client/go/cmd/command_tester.go b/client/go/cmd/command_tester.go
index f455ffa9957..6929b59decb 100644
--- a/client/go/cmd/command_tester.go
+++ b/client/go/cmd/command_tester.go
@@ -22,6 +22,7 @@ import (
type command struct {
homeDir string
+ cacheDir string
args []string
moreArgs []string
}
@@ -31,12 +32,16 @@ func execute(cmd command, t *testing.T, client *mockHttpClient) (string, string)
util.ActiveHttpClient = client
}
- // Set config dir. Use a separate one per test if none is specified
+ // Set Vespa CLI directories. Use a separate one per test if none is specified
if cmd.homeDir == "" {
- cmd.homeDir = t.TempDir()
+ cmd.homeDir = filepath.Join(t.TempDir(), ".vespa")
viper.Reset()
}
- os.Setenv("VESPA_CLI_HOME", filepath.Join(cmd.homeDir, ".vespa"))
+ if cmd.cacheDir == "" {
+ cmd.cacheDir = filepath.Join(t.TempDir(), ".cache", "vespa")
+ }
+ os.Setenv("VESPA_CLI_HOME", cmd.homeDir)
+ os.Setenv("VESPA_CLI_CACHE_DIR", cmd.cacheDir)
// Reset flags to their default value - persistent flags in Cobra persists over tests
rootCmd.Flags().VisitAll(func(f *pflag.Flag) {
@@ -111,5 +116,3 @@ func (c *mockHttpClient) Do(request *http.Request, timeout time.Duration) (*http
}
func (c *mockHttpClient) UseCertificate(certificate tls.Certificate) {}
-
-func convergeServices(client *mockHttpClient) { client.NextResponse(200, `{"converged":true}`) }
diff --git a/client/go/cmd/config.go b/client/go/cmd/config.go
index 3753d9a9390..863f247bd7c 100644
--- a/client/go/cmd/config.go
+++ b/client/go/cmd/config.go
@@ -34,8 +34,16 @@ func init() {
}
var configCmd = &cobra.Command{
- Use: "config",
- Short: "Configure default values for flags",
+ Use: "config",
+ Short: "Configure persistent values for flags",
+ Long: `Configure persistent values for flags.
+
+This command allows setting a persistent value for a given flag. On future
+invocations the flag can then be omitted as it is read from the config file
+instead.
+
+Configuration is written to $HOME/.vespa by default. This path can be
+overridden by setting the VESPA_CLI_HOME environment variable.`,
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
// Root command does nothing
@@ -100,16 +108,8 @@ type Config struct {
}
func LoadConfig() (*Config, error) {
- home := os.Getenv("VESPA_CLI_HOME")
- if home == "" {
- var err error
- home, err = os.UserHomeDir()
- if err != nil {
- return nil, err
- }
- home = filepath.Join(home, ".vespa")
- }
- if err := os.MkdirAll(home, 0700); err != nil {
+ home, err := vespaCliHome()
+ if err != nil {
return nil, err
}
c := &Config{Home: home, createDirs: true}
diff --git a/client/go/cmd/config_test.go b/client/go/cmd/config_test.go
index cf50f561f0f..25ba7cc0655 100644
--- a/client/go/cmd/config_test.go
+++ b/client/go/cmd/config_test.go
@@ -1,13 +1,14 @@
package cmd
import (
+ "path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestConfig(t *testing.T) {
- homeDir := t.TempDir()
+ homeDir := filepath.Join(t.TempDir(), ".vespa")
assertConfigCommand(t, "invalid option or value: \"foo\": \"bar\"\n", homeDir, "config", "set", "foo", "bar")
assertConfigCommand(t, "foo = <unset>\n", homeDir, "config", "get", "foo")
assertConfigCommand(t, "target = local\n", homeDir, "config", "get", "target")
diff --git a/client/go/cmd/curl_test.go b/client/go/cmd/curl_test.go
index 340eacd0bd3..d5021e19cf2 100644
--- a/client/go/cmd/curl_test.go
+++ b/client/go/cmd/curl_test.go
@@ -10,13 +10,12 @@ import (
)
func TestCurl(t *testing.T) {
- homeDir := t.TempDir()
+ homeDir := filepath.Join(t.TempDir(), ".vespa")
httpClient := &mockHttpClient{}
- convergeServices(httpClient)
out, _ := execute(command{homeDir: homeDir, args: []string{"curl", "-n", "-a", "t1.a1.i1", "--", "-v", "--data-urlencode", "arg=with space", "/search"}}, t, httpClient)
expected := fmt.Sprintf("curl --key %s --cert %s -v --data-urlencode 'arg=with space' https://127.0.0.1:8080/search\n",
- filepath.Join(homeDir, ".vespa", "t1.a1.i1", "data-plane-private-key.pem"),
- filepath.Join(homeDir, ".vespa", "t1.a1.i1", "data-plane-public-cert.pem"))
+ filepath.Join(homeDir, "t1.a1.i1", "data-plane-private-key.pem"),
+ filepath.Join(homeDir, "t1.a1.i1", "data-plane-public-cert.pem"))
assert.Equal(t, expected, out)
}
diff --git a/client/go/cmd/deploy.go b/client/go/cmd/deploy.go
index 9bf59187778..b3171d184e0 100644
--- a/client/go/cmd/deploy.go
+++ b/client/go/cmd/deploy.go
@@ -39,7 +39,11 @@ When this returns successfully the application package has been validated
and activated on config servers. The process of applying it on individual nodes
has started but may not have completed.
-If application directory is not specified, it defaults to working directory.`,
+If application directory is not specified, it defaults to working directory.
+
+When deploying to Vespa Cloud the system can be overridden by setting the
+environment variable VESPA_CLI_CLOUD_SYSTEM. This is intended for internal use
+only.`,
Example: "$ vespa deploy .",
Args: cobra.MaximumNArgs(1),
DisableAutoGenTag: true,
@@ -78,7 +82,7 @@ If application directory is not specified, it defaults to working directory.`,
if opts.IsCloud() {
log.Printf("\nUse %s for deployment status, or follow this deployment at", color.Cyan("vespa status"))
log.Print(color.Cyan(fmt.Sprintf("%s/tenant/%s/application/%s/dev/instance/%s/job/%s-%s/run/%d",
- defaultConsoleURL,
+ getConsoleURL(),
opts.Deployment.Application.Tenant, opts.Deployment.Application.Application, opts.Deployment.Application.Instance,
opts.Deployment.Zone.Environment, opts.Deployment.Zone.Region,
sessionOrRunID)))
diff --git a/client/go/cmd/deploy_test.go b/client/go/cmd/deploy_test.go
index 443f7e8846f..9614806b968 100644
--- a/client/go/cmd/deploy_test.go
+++ b/client/go/cmd/deploy_test.go
@@ -130,7 +130,7 @@ func assertActivate(applicationPackage string, arguments []string, t *testing.T)
if err := cfg.WriteSessionID(vespa.DefaultApplication, 42); err != nil {
t.Fatal(err)
}
- out, _ := execute(command{args: arguments, homeDir: homeDir}, t, client)
+ out, _ := execute(command{args: arguments, homeDir: cfg.Home}, t, client)
assert.Equal(t,
"Success: Activated "+applicationPackage+" with session 42\n",
out)
diff --git a/client/go/cmd/document_test.go b/client/go/cmd/document_test.go
index 8aecb538f89..1f82b85f915 100644
--- a/client/go/cmd/document_test.go
+++ b/client/go/cmd/document_test.go
@@ -67,7 +67,6 @@ func TestDocumentRemoveWithoutIdArg(t *testing.T) {
func TestDocumentSendMissingId(t *testing.T) {
arguments := []string{"document", "put", "testdata/A-Head-Full-of-Dreams-Without-Operation.json"}
client := &mockHttpClient{}
- convergeServices(client)
assert.Equal(t,
"Error: No document id given neither as argument or as a 'put' key in the json file\n",
executeCommand(t, client, arguments, []string{}))
@@ -76,7 +75,6 @@ func TestDocumentSendMissingId(t *testing.T) {
func TestDocumentSendWithDisagreeingOperations(t *testing.T) {
arguments := []string{"document", "update", "testdata/A-Head-Full-of-Dreams-Put.json"}
client := &mockHttpClient{}
- convergeServices(client)
assert.Equal(t,
"Error: Wanted document operation is update but the JSON file specifies put\n",
executeCommand(t, client, arguments, []string{}))
@@ -140,7 +138,6 @@ func assertDocumentGet(arguments []string, documentId string, t *testing.T) {
func assertDocumentError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{}
- convergeServices(client)
client.NextResponse(status, errorMessage)
assert.Equal(t,
"Error: Invalid document operation: Status "+strconv.Itoa(status)+"\n\n"+errorMessage+"\n",
@@ -151,7 +148,6 @@ func assertDocumentError(t *testing.T, status int, errorMessage string) {
func assertDocumentServerError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{}
- convergeServices(client)
client.NextResponse(status, errorMessage)
assert.Equal(t,
"Error: Container (document API) at 127.0.0.1:8080: Status "+strconv.Itoa(status)+"\n\n"+errorMessage+"\n",
@@ -161,6 +157,5 @@ func assertDocumentServerError(t *testing.T, status int, errorMessage string) {
}
func documentServiceURL(client *mockHttpClient) string {
- convergeServices(client)
return getService("document", 0).BaseURL
}
diff --git a/client/go/cmd/helpers.go b/client/go/cmd/helpers.go
index f29a842aed2..98d6814d16f 100644
--- a/client/go/cmd/helpers.go
+++ b/client/go/cmd/helpers.go
@@ -10,14 +10,13 @@ import (
"io/ioutil"
"log"
"os"
+ "path/filepath"
"strings"
"time"
"github.com/vespa-engine/vespa/client/go/vespa"
)
-const defaultConsoleURL = "https://console.vespa.oath.cloud"
-
var exitFunc = os.Exit // To allow overriding Exit in tests
func fatalErrHint(err error, hints ...string) {
@@ -50,6 +49,36 @@ func printSuccess(msg ...interface{}) {
log.Print(color.Green("Success: "), fmt.Sprint(msg...))
}
+func vespaCliHome() (string, error) {
+ home := os.Getenv("VESPA_CLI_HOME")
+ if home == "" {
+ userHome, err := os.UserHomeDir()
+ if err != nil {
+ return "", err
+ }
+ home = filepath.Join(userHome, ".vespa")
+ }
+ if err := os.MkdirAll(home, 0700); err != nil {
+ return "", err
+ }
+ return home, nil
+}
+
+func vespaCliCacheDir() (string, error) {
+ cacheDir := os.Getenv("VESPA_CLI_CACHE_DIR")
+ if cacheDir == "" {
+ userCacheDir, err := os.UserCacheDir()
+ if err != nil {
+ return "", err
+ }
+ cacheDir = filepath.Join(userCacheDir, "vespa")
+ }
+ if err := os.MkdirAll(cacheDir, 0755); err != nil {
+ return "", err
+ }
+ return cacheDir, nil
+}
+
func deploymentFromArgs() vespa.Deployment {
zone, err := vespa.ZoneFromString(zoneArg)
if err != nil {
@@ -102,18 +131,32 @@ func getService(service string, sessionOrRunID int64) *vespa.Service {
t := getTarget()
timeout := time.Duration(waitSecsArg) * time.Second
if timeout > 0 {
- log.Printf("Waiting up to %d %s for services to become available ...", color.Cyan(waitSecsArg), color.Cyan("seconds"))
- }
- if err := t.DiscoverServices(timeout, sessionOrRunID); err != nil {
- fatalErr(err, "Services unavailable")
+ log.Printf("Waiting up to %d %s for service to become available ...", color.Cyan(waitSecsArg), color.Cyan("seconds"))
}
- s, err := t.Service(service)
+ s, err := t.Service(service, timeout, sessionOrRunID)
if err != nil {
- fatalErr(err, "Invalid service")
+ fatalErr(err, "Invalid service: ", service)
}
return s
}
+func getConsoleURL() string {
+ system := os.Getenv("VESPA_CLI_CLOUD_SYSTEM")
+ if system == "publiccd" {
+ return "https://console-cd.vespa.oath.cloud"
+ }
+ return "https://console.vespa.oath.cloud"
+
+}
+
+func getApiURL() string {
+ system := os.Getenv("VESPA_CLI_CLOUD_SYSTEM")
+ if system == "publiccd" {
+ return "https://api.vespa-external-cd.aws.oath.cloud:4443"
+ }
+ return "https://api.vespa-external.aws.oath.cloud:4443"
+}
+
func getTarget() vespa.Target {
targetType := getTargetType()
if strings.HasPrefix(targetType, "http") {
@@ -147,7 +190,7 @@ func getTarget() vespa.Target {
if err != nil {
fatalErrHint(err, "Deployment to cloud requires a certificate. Try 'vespa cert'")
}
- return vespa.CloudTarget(deployment, apiKey,
+ return vespa.CloudTarget(getApiURL(), deployment, apiKey,
vespa.TLSOptions{
KeyPair: kp,
CertificateFile: certificateFile,
diff --git a/client/go/cmd/query_test.go b/client/go/cmd/query_test.go
index bd9ae91f24d..137ffa01cd5 100644
--- a/client/go/cmd/query_test.go
+++ b/client/go/cmd/query_test.go
@@ -56,7 +56,6 @@ func assertQuery(t *testing.T, expectedQuery string, query ...string) {
func assertQueryError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{}
- convergeServices(client)
client.NextResponse(status, errorMessage)
assert.Equal(t,
"Error: Invalid query: Status "+strconv.Itoa(status)+"\n"+errorMessage+"\n",
@@ -66,7 +65,6 @@ func assertQueryError(t *testing.T, status int, errorMessage string) {
func assertQueryServiceError(t *testing.T, status int, errorMessage string) {
client := &mockHttpClient{}
- convergeServices(client)
client.NextResponse(status, errorMessage)
assert.Equal(t,
"Error: Status "+strconv.Itoa(status)+" from container at 127.0.0.1:8080\n"+errorMessage+"\n",
@@ -75,6 +73,5 @@ func assertQueryServiceError(t *testing.T, status int, errorMessage string) {
}
func queryServiceURL(client *mockHttpClient) string {
- convergeServices(client)
return getService("query", 0).BaseURL
}
diff --git a/client/go/cmd/status_test.go b/client/go/cmd/status_test.go
index 8ddca71a35b..0c1c8e4e3a7 100644
--- a/client/go/cmd/status_test.go
+++ b/client/go/cmd/status_test.go
@@ -44,7 +44,6 @@ func TestStatusErrorResponse(t *testing.T) {
func assertDeployStatus(target string, args []string, t *testing.T) {
client := &mockHttpClient{}
- convergeServices(client)
assert.Equal(t,
"Deploy API at "+target+" is ready\n",
executeCommand(t, client, []string{"status", "deploy"}, args),
@@ -54,14 +53,12 @@ func assertDeployStatus(target string, args []string, t *testing.T) {
func assertQueryStatus(target string, args []string, t *testing.T) {
client := &mockHttpClient{}
- convergeServices(client)
assert.Equal(t,
"Container (query API) at "+target+" is ready\n",
executeCommand(t, client, []string{"status", "query"}, args),
"vespa status container")
assert.Equal(t, target+"/ApplicationStatus", client.lastRequest.URL.String())
- convergeServices(client)
assert.Equal(t,
"Container (query API) at "+target+" is ready\n",
executeCommand(t, client, []string{"status"}, args),
@@ -71,7 +68,6 @@ func assertQueryStatus(target string, args []string, t *testing.T) {
func assertDocumentStatus(target string, args []string, t *testing.T) {
client := &mockHttpClient{}
- convergeServices(client)
assert.Equal(t,
"Container (document API) at "+target+" is ready\n",
executeCommand(t, client, []string{"status", "document"}, args),
@@ -81,7 +77,6 @@ func assertDocumentStatus(target string, args []string, t *testing.T) {
func assertQueryStatusError(target string, args []string, t *testing.T) {
client := &mockHttpClient{}
- convergeServices(client)
client.NextStatus(500)
assert.Equal(t,
"Container (query API) at "+target+" is not ready\nStatus 500\n",
diff --git a/client/go/vespa/deploy.go b/client/go/vespa/deploy.go
index ece841617c0..19319724d18 100644
--- a/client/go/vespa/deploy.go
+++ b/client/go/vespa/deploy.go
@@ -71,7 +71,7 @@ func (d DeploymentOpts) String() string {
func (d *DeploymentOpts) IsCloud() bool { return d.Target.Type() == cloudTargetType }
func (d *DeploymentOpts) url(path string) (*url.URL, error) {
- service, err := d.Target.Service("deploy")
+ service, err := d.Target.Service(deployService, 0, 0)
if err != nil {
return nil, err
}
diff --git a/client/go/vespa/target.go b/client/go/vespa/target.go
index 69dc876c1c8..df9144cd186 100644
--- a/client/go/vespa/target.go
+++ b/client/go/vespa/target.go
@@ -24,8 +24,6 @@ const (
queryService = "query"
documentService = "document"
- defaultCloudAPI = "https://api.vespa-external.aws.oath.cloud:4443"
-
waitRetryInterval = 2 * time.Second
)
@@ -41,11 +39,8 @@ type Target interface {
// Type returns this target's type, e.g. local or cloud.
Type() string
- // Service returns the service for given name.
- Service(name string) (*Service, error)
-
- // DiscoverServices queries for services available on this target after the deployment run has completed.
- DiscoverServices(timeout time.Duration, runID int64) error
+ // Service returns the service for given name. If timeout is non-zero, wait for the service to converge.
+ Service(name string, timeout time.Duration, sessionOrRunID int64) (*Service, error)
}
// TLSOptions configures the certificate to use for service requests.
@@ -107,7 +102,12 @@ func (s *Service) Description() string {
func (t *customTarget) Type() string { return t.targetType }
-func (t *customTarget) Service(name string) (*Service, error) {
+func (t *customTarget) Service(name string, timeout time.Duration, sessionID int64) (*Service, error) {
+ if timeout > 0 && name != deployService {
+ if err := t.waitForConvergence(timeout); err != nil {
+ return nil, err
+ }
+ }
switch name {
case deployService, queryService, documentService:
url, err := t.urlWithPort(name)
@@ -139,12 +139,12 @@ func (t *customTarget) urlWithPort(serviceName string) (string, error) {
return u.String(), nil
}
-func (t *customTarget) DiscoverServices(timeout time.Duration, runID int64) error {
- deployService, err := t.Service("deploy")
+func (t *customTarget) waitForConvergence(timeout time.Duration) error {
+ deployer, err := t.Service(deployService, 0, 0)
if err != nil {
return err
}
- url := fmt.Sprintf("%s/application/v2/tenant/default/application/default/environment/prod/region/default/instance/default/serviceconverge", deployService.BaseURL)
+ url := fmt.Sprintf("%s/application/v2/tenant/default/application/default/environment/prod/region/default/instance/default/serviceconverge", deployer.BaseURL)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
@@ -171,7 +171,7 @@ func (t *customTarget) DiscoverServices(timeout time.Duration, runID int64) erro
}
type cloudTarget struct {
- cloudAPI string
+ apiURL string
targetType string
deployment Deployment
apiKey []byte
@@ -184,27 +184,30 @@ type cloudTarget struct {
func (t *cloudTarget) Type() string { return t.targetType }
-func (t *cloudTarget) Service(name string) (*Service, error) {
+func (t *cloudTarget) Service(name string, timeout time.Duration, runID int64) (*Service, error) {
+ if timeout > 0 && name != deployService {
+ if err := t.waitForEndpoints(timeout, runID); err != nil {
+ return nil, err
+ }
+ }
switch name {
case deployService:
- return &Service{Name: name, BaseURL: t.cloudAPI}, nil
+ return &Service{Name: name, BaseURL: t.apiURL}, nil
case queryService:
if t.queryURL == "" {
- return nil, fmt.Errorf("service %s not discovered", name)
+ return nil, fmt.Errorf("service %s is not discovered", name)
}
return &Service{Name: name, BaseURL: t.queryURL, TLSOptions: t.tlsOptions}, nil
case documentService:
if t.documentURL == "" {
- return nil, fmt.Errorf("service %s not discovered", name)
+ return nil, fmt.Errorf("service %s is not discovered", name)
}
return &Service{Name: name, BaseURL: t.documentURL, TLSOptions: t.tlsOptions}, nil
}
return nil, fmt.Errorf("unknown service: %s", name)
}
-// DiscoverServices waits for run identified by runID to complete and at least one endpoint is available, or timeout
-// passes.
-func (t *cloudTarget) DiscoverServices(timeout time.Duration, runID int64) error {
+func (t *cloudTarget) waitForEndpoints(timeout time.Duration, runID int64) error {
signer := NewRequestSigner(t.deployment.Application.SerializedForm(), t.apiKey)
if runID > 0 {
if err := t.waitForRun(signer, runID, timeout); err != nil {
@@ -216,7 +219,7 @@ func (t *cloudTarget) DiscoverServices(timeout time.Duration, runID int64) error
func (t *cloudTarget) waitForRun(signer *RequestSigner, runID int64, timeout time.Duration) error {
runURL := fmt.Sprintf("%s/application/v4/tenant/%s/application/%s/instance/%s/job/%s-%s/run/%d",
- t.cloudAPI,
+ t.apiURL,
t.deployment.Application.Tenant, t.deployment.Application.Application, t.deployment.Application.Instance,
t.deployment.Zone.Environment, t.deployment.Zone.Region, runID)
req, err := http.NewRequest("GET", runURL, nil)
@@ -234,8 +237,8 @@ func (t *cloudTarget) waitForRun(signer *RequestSigner, runID int64, timeout tim
return req
}
jobSuccessFunc := func(status int, response []byte) (bool, error) {
- if status/100 != 2 {
- return false, nil
+ if ok, err := isOK(status); !ok {
+ return ok, err
}
var resp jobResponse
if err := json.Unmarshal(response, &resp); err != nil {
@@ -280,7 +283,7 @@ func (t *cloudTarget) printLog(response jobResponse, last int64) int64 {
func (t *cloudTarget) discoverEndpoints(signer *RequestSigner, timeout time.Duration) error {
deploymentURL := fmt.Sprintf("%s/application/v4/tenant/%s/application/%s/instance/%s/environment/%s/region/%s",
- t.cloudAPI,
+ t.apiURL,
t.deployment.Application.Tenant, t.deployment.Application.Application, t.deployment.Application.Instance,
t.deployment.Zone.Environment, t.deployment.Zone.Region)
req, err := http.NewRequest("GET", deploymentURL, nil)
@@ -292,8 +295,8 @@ func (t *cloudTarget) discoverEndpoints(signer *RequestSigner, timeout time.Dura
}
var endpointURL string
endpointFunc := func(status int, response []byte) (bool, error) {
- if status/100 != 2 {
- return false, nil
+ if ok, err := isOK(status); !ok {
+ return ok, err
}
var resp deploymentResponse
if err := json.Unmarshal(response, &resp); err != nil {
@@ -316,6 +319,13 @@ func (t *cloudTarget) discoverEndpoints(signer *RequestSigner, timeout time.Dura
return nil
}
+func isOK(status int) (bool, error) {
+ if status == 401 {
+ return false, fmt.Errorf("status %d: invalid api key", status)
+ }
+ return status/100 == 2, nil
+}
+
// LocalTarget creates a target for a Vespa platform running locally.
func LocalTarget() Target {
return &customTarget{targetType: localTargetType, baseURL: "http://127.0.0.1"}
@@ -327,9 +337,9 @@ func CustomTarget(baseURL string) Target {
}
// CloudTarget creates a Target for the Vespa Cloud platform.
-func CloudTarget(deployment Deployment, apiKey []byte, tlsOptions TLSOptions, logOptions LogOptions) Target {
+func CloudTarget(apiURL string, deployment Deployment, apiKey []byte, tlsOptions TLSOptions, logOptions LogOptions) Target {
return &cloudTarget{
- cloudAPI: defaultCloudAPI,
+ apiURL: apiURL,
targetType: cloudTargetType,
deployment: deployment,
apiKey: apiKey,
@@ -409,7 +419,8 @@ func wait(fn responseFunc, reqFn requestFunc, certificate *tls.Certificate, time
return statusCode, nil
}
}
- if loopOnce {
+ timeLeft := deadline.Sub(time.Now())
+ if loopOnce || timeLeft < waitRetryInterval {
break
}
time.Sleep(waitRetryInterval)
diff --git a/client/go/vespa/target_test.go b/client/go/vespa/target_test.go
index 31f145f0db3..2c90baefbbc 100644
--- a/client/go/vespa/target_test.go
+++ b/client/go/vespa/target_test.go
@@ -74,11 +74,11 @@ func TestCustomTargetWait(t *testing.T) {
defer srv.Close()
target := CustomTarget(srv.URL)
- err := target.DiscoverServices(0, 42)
+ _, err := target.Service("query", time.Millisecond, 42)
assert.NotNil(t, err)
vc.deploymentConverged = true
- err = target.DiscoverServices(0, 42)
+ _, err = target.Service("query", time.Millisecond, 42)
assert.Nil(t, err)
assertServiceWait(t, 200, target, "deploy")
@@ -102,6 +102,7 @@ func TestCloudTargetWait(t *testing.T) {
var logWriter bytes.Buffer
target := CloudTarget(
+ "https://example.com",
Deployment{
Application: ApplicationID{Tenant: "t1", Application: "a1", Instance: "i1"},
Zone: ZoneID{Environment: "dev", Region: "us-north-1"},
@@ -110,20 +111,17 @@ func TestCloudTargetWait(t *testing.T) {
TLSOptions{KeyPair: x509KeyPair},
LogOptions{Writer: &logWriter})
if ct, ok := target.(*cloudTarget); ok {
- ct.cloudAPI = srv.URL
+ ct.apiURL = srv.URL
} else {
t.Fatalf("Wrong target type %T", ct)
}
assertServiceWait(t, 200, target, "deploy")
- _, err = target.Service("query")
- assert.NotNil(t, err)
-
- err = target.DiscoverServices(0, 42)
+ _, err = target.Service("query", time.Millisecond, 42)
assert.NotNil(t, err)
vc.deploymentConverged = true
- err = target.DiscoverServices(0, 42)
+ _, err = target.Service("query", time.Millisecond, 42)
assert.Nil(t, err)
assertServiceWait(t, 500, target, "query")
@@ -136,13 +134,13 @@ func TestCloudTargetWait(t *testing.T) {
}
func assertServiceURL(t *testing.T, url string, target Target, service string) {
- s, err := target.Service(service)
+ s, err := target.Service(service, 0, 42)
assert.Nil(t, err)
assert.Equal(t, url, s.BaseURL)
}
func assertServiceWait(t *testing.T, expectedStatus int, target Target, service string) {
- s, err := target.Service(service)
+ s, err := target.Service(service, 0, 42)
assert.Nil(t, err)
status, err := s.Wait(0)
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
index 2870b88d105..6d5c7a27a47 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java
@@ -80,13 +80,10 @@ public interface ModelContext {
@ModelFeatureFlag(owners = {"baldersheim"}) default boolean useAsyncMessageHandlingOnSchedule() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}) default double feedConcurrency() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}) default int metricsproxyNumThreads() { throw new UnsupportedOperationException("TODO specify default value"); }
- @ModelFeatureFlag(owners = {"baldersheim"}) default boolean enforceRankProfileInheritance() { return false; }
+ @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter = "7.472") default boolean enforceRankProfileInheritance() { return true; }
@ModelFeatureFlag(owners = {"baldersheim"}) default int largeRankExpressionLimit() { return 8192; }
- @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter = "7.468") default boolean useExternalRankExpressions() { return true; }
- @ModelFeatureFlag(owners = {"baldersheim"}, removeAfter = "7.468") default boolean distributeExternalRankExpressions() { return true; }
@ModelFeatureFlag(owners = {"baldersheim"}) default int maxConcurrentMergesPerNode() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}) default int maxMergeQueueSize() { throw new UnsupportedOperationException("TODO specify default value"); }
- @ModelFeatureFlag(owners = {"baldersheim"}) default boolean dryRunOnnxOnSetup() { return true; }
@ModelFeatureFlag(owners = {"baldersheim"}) default boolean containerDumpHeapOnShutdownTimeout() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"baldersheim"}) default double containerShutdownTimeout() { throw new UnsupportedOperationException("TODO specify default value"); }
@ModelFeatureFlag(owners = {"geirst"}) default boolean enableFeedBlockInDistributor() { return true; }
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
index dea6b980692..e27e0e7624f 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java
@@ -107,7 +107,6 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
@Override public double resourceLimitMemory() { return resourceLimitMemory; }
@Override public double minNodeRatioPerGroup() { return minNodeRatioPerGroup; }
@Override public int metricsproxyNumThreads() { return 1; }
- @Override public boolean enforceRankProfileInheritance() { return enforceRankProfileInheritance; }
@Override public double containerShutdownTimeout() { return containerShutdownTimeout; }
@Override public boolean containerDumpHeapOnShutdownTimeout() { return containerDumpHeapOnShutdownTimeout; }
public TestProperties containerDumpHeapOnShutdownTimeout(boolean value) {
@@ -118,10 +117,6 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea
containerShutdownTimeout = value;
return this;
}
- public TestProperties enforceRankProfileInheritance(boolean value) {
- enforceRankProfileInheritance = value;
- return this;
- }
public TestProperties largeRankExpressionLimit(int value) {
largeRankExpressionLimit = value;
return this;
diff --git a/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java b/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java
index 2784c111019..decc6e98bc4 100644
--- a/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java
+++ b/config-model/src/main/java/com/yahoo/config/model/producer/AbstractConfigProducer.java
@@ -223,15 +223,11 @@ public abstract class AbstractConfigProducer<CHILD extends AbstractConfigProduce
didApply = parent.addUserConfig(builder);
}
- if (log.isLoggable(Level.FINEST)) {
- log.log(Level.FINEST, "User configs is: " + userConfigs.toString());
- }
+ log.log(Level.FINEST, () -> "User configs is: " + userConfigs.toString());
// TODO: What do we do with md5. Currently ignored for user configs?
ConfigDefinitionKey key = new ConfigDefinitionKey(builder.getDefName(), builder.getDefNamespace());
if (userConfigs.get(key) != null) {
- if (log.isLoggable(Level.FINEST)) {
- log.log(Level.FINEST, "Apply in " + configId);
- }
+ log.log(Level.FINEST, () -> "Apply in " + configId);
applyUserConfig(builder, userConfigs.get(key));
didApply = true;
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/DistributableResource.java b/config-model/src/main/java/com/yahoo/searchdefinition/DistributableResource.java
index 1719ea72cb0..472bc9d5413 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/DistributableResource.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/DistributableResource.java
@@ -11,6 +11,7 @@ import java.util.Collection;
import java.util.Objects;
public class DistributableResource {
+
public enum PathType { FILE, URI, BLOB };
/** The search definition-unique name of this constant */
@@ -95,10 +96,9 @@ public class DistributableResource {
}
}
+ @Override
public String toString() {
- StringBuilder b = new StringBuilder();
- b.append("resource '").append(name).append(" of type '").append(pathType)
- .append("' with ref '").append(fileReference).append("'");
- return b.toString();
+ return "resource '" + name + " of type '" + pathType + "' with ref '" + fileReference + "'";
}
+
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
index 9d51d39f3d0..d169760538d 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java
@@ -227,12 +227,7 @@ public class RankProfile implements Cloneable {
String msg = "rank-profile '" + getName() + "' inherits '" + inheritedName +
"', but it does not exist anywhere in the inheritance of search '" +
((getSearch() != null) ? getSearch().getName() : " global rank profiles") + "'.";
- if (search.getDeployProperties().featureFlags().enforceRankProfileInheritance()) {
- throw new IllegalArgumentException(msg);
- } else {
- deployLogger.logApplicationPackage(Level.WARNING, msg);
- inherited = resolveIndependentOfInheritance();
- }
+ throw new IllegalArgumentException(msg);
} else {
List<String> children = new ArrayList<>();
children.add(createFullyQualifiedName());
@@ -241,12 +236,7 @@ public class RankProfile implements Cloneable {
}
return inherited;
}
- private RankProfile resolveIndependentOfInheritance() {
- for (RankProfile rankProfile : rankProfileRegistry.all()) {
- if (rankProfile.getName().equals(inheritedName)) return rankProfile;
- }
- return null;
- }
+
private String createFullyQualifiedName() {
return (search != null)
? (search.getName() + "." + getName())
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java
index 46b785ccf42..ad85f68cb8a 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/RankProfileList.java
@@ -100,12 +100,6 @@ public class RankProfileList extends Derived implements RankProfilesConfig.Produ
remaining.forEach((name, rank) -> {
if (areDependenciesReady(rank, rankProfileRegistry)) ready.add(rank);
});
- if (ready.isEmpty() && ! deployProperties.featureFlags().enforceRankProfileInheritance()) {
- // Dirty fallback to allow incorrect rankprofile inheritance to pass for now.
- // We then handle one by one.
- // TODO remove ASAP
- ready.add(remaining.values().iterator().next());
- }
processRankProfiles(ready, queryProfiles, importedModels, search, attributeFields, deployProperties, executor);
ready.forEach(rank -> remaining.remove(rank.getName()));
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingOperation.java b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingOperation.java
index 4332d8baea8..87fa74b92fe 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingOperation.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/fieldoperation/IndexingOperation.java
@@ -11,6 +11,7 @@ import com.yahoo.vespa.indexinglanguage.ScriptParserContext;
import com.yahoo.vespa.indexinglanguage.expressions.ScriptExpression;
import com.yahoo.vespa.indexinglanguage.expressions.StatementExpression;
import com.yahoo.vespa.indexinglanguage.linguistics.AnnotatorConfig;
+import com.yahoo.yolean.Exceptions;
/**
* @author Einar M R Rosenvinge
@@ -46,7 +47,7 @@ public class IndexingOperation implements FieldOperation {
exp = new ScriptExpression(StatementExpression.newInstance(config));
}
} catch (com.yahoo.vespa.indexinglanguage.parser.ParseException e) {
- ParseException t = new ParseException("Error reported by IL parser: " + e.getMessage());
+ ParseException t = new ParseException("Could not parse indexing statement: " + Exceptions.toMessageString(e));
t.initCause(e);
throw t;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
index 937537e5f99..2c87fd5c5b3 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModel.java
@@ -492,8 +492,8 @@ public final class VespaModel extends AbstractConfigProducerRoot implements Seri
boolean found = configProducer.cascadeConfig(builder);
boolean foundOverride = configProducer.addUserConfig(builder);
log.log(Level.FINE, () -> "Trying to get config for " + builder.getClass().getDeclaringClass().getName() +
- " for config id " + quote(configProducer.getConfigId()) +
- ", found=" + found + ", foundOverride=" + foundOverride);
+ " for config id " + quote(configProducer.getConfigId()) +
+ ", found=" + found + ", foundOverride=" + foundOverride);
}
/**
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java
index ed1dc80d71d..af88d9c008a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/clustercontroller/ClusterControllerContainer.java
@@ -23,7 +23,7 @@ import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.SimpleComponent;
import com.yahoo.vespa.model.container.component.SystemBindingPattern;
import com.yahoo.vespa.model.container.xml.ContainerModelBuilder;
-import com.yahoo.vespa.model.container.xml.PlatformBundles;
+import com.yahoo.vespa.model.container.PlatformBundles;
import java.util.Set;
import java.util.TreeSet;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
index 0b2d0936235..14ead1cdece 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java
@@ -38,7 +38,7 @@ import com.yahoo.vespa.model.admin.monitoring.Monitoring;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.SystemBindingPattern;
-import com.yahoo.vespa.model.container.xml.PlatformBundles;
+import com.yahoo.vespa.model.container.PlatformBundles;
import java.nio.file.Path;
import java.util.Collections;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
index 1dd7b35cda5..5574082e334 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
@@ -34,7 +34,6 @@ import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.Servlet;
import com.yahoo.vespa.model.container.component.SystemBindingPattern;
import com.yahoo.vespa.model.container.configserver.ConfigserverCluster;
-import com.yahoo.vespa.model.container.xml.PlatformBundles;
import com.yahoo.vespa.model.utils.FileSender;
import java.util.ArrayList;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
index 532ac78f17e..f5b168958c0 100755
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
@@ -171,6 +171,8 @@ public abstract class ContainerCluster<CONTAINER extends Container>
componentGroup = new ComponentGroup<>(this, "component");
+ addCommonVespaBundles();
+
addComponent(new StatisticsComponent());
addSimpleComponent(AccessLog.class);
addComponent(new DefaultThreadpoolProvider(this, deployState.featureFlags().metricsproxyNumThreads()));
@@ -459,6 +461,13 @@ public abstract class ContainerCluster<CONTAINER extends Container>
}
/**
+ * Adds the Vespa bundles that are necessary for all container types.
+ */
+ public void addCommonVespaBundles() {
+ PlatformBundles.commonVespaBundles().forEach(this::addPlatformBundle);
+ }
+
+ /**
* Adds a bundle present at a known location at the target container nodes.
* Note that the set of platform bundles cannot change during the jdisc container's lifetime.
*
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java
index 65247f29281..25cb684932b 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerModelEvaluation.java
@@ -10,7 +10,6 @@ import com.yahoo.vespa.config.search.core.RankingConstantsConfig;
import com.yahoo.vespa.config.search.core.RankingExpressionsConfig;
import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.SystemBindingPattern;
-import com.yahoo.vespa.model.container.xml.PlatformBundles;
import java.nio.file.Path;
import java.util.List;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/PlatformBundles.java b/config-model/src/main/java/com/yahoo/vespa/model/container/PlatformBundles.java
new file mode 100644
index 00000000000..24412027e63
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/PlatformBundles.java
@@ -0,0 +1,133 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.model.container;
+
+import com.yahoo.vespa.defaults.Defaults;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * @author gjoranv
+ * @author Ulf Lilleengen
+ */
+public class PlatformBundles {
+
+ private enum JarSuffix {
+ JAR_WITH_DEPS("-jar-with-dependencies.jar"),
+ DEPLOY("-deploy.jar");
+
+ public final String suffix;
+
+ JarSuffix(String suffix) {
+ this.suffix = suffix;
+ }
+ }
+
+ public static final Path LIBRARY_PATH = Paths.get(Defaults.getDefaults().underVespaHome("lib/jars"));
+ public static final String searchAndDocprocBundle = "container-search-and-docproc";
+
+ public static Set<Path> commonVespaBundles() {
+ var bundles = new LinkedHashSet<Path>();
+ commonVespaBundles.stream().map(PlatformBundles::absoluteBundlePath).forEach(bundles::add);
+ return Collections.unmodifiableSet(bundles);
+ }
+
+ public static Path absoluteBundlePath(String fileName) {
+ return absoluteBundlePath(fileName, JarSuffix.JAR_WITH_DEPS);
+ }
+
+ public static Path absoluteBundlePath(String fileName, JarSuffix jarSuffix) {
+ if (fileName == null) return null;
+ return LIBRARY_PATH.resolve(Paths.get(fileName + jarSuffix.suffix));
+ }
+
+ public static boolean isSearchAndDocprocClass(String className) {
+ return searchAndDocprocComponents.contains(className);
+ }
+
+ // Bundles that must be loaded for all container types.
+ private static final Set<String> commonVespaBundles = Set.of(
+ "zkfacade",
+ "zookeeper-server" // TODO: not necessary in metrics-proxy.
+ );
+
+ // This is a hack to allow users to declare components from the search-and-docproc bundle without naming the bundle.
+ private static final Set<String> searchAndDocprocComponents = Set.of(
+ "com.yahoo.docproc.AbstractConcreteDocumentFactory",
+ "com.yahoo.docproc.DocumentProcessor",
+ "com.yahoo.docproc.SimpleDocumentProcessor",
+ "com.yahoo.docproc.util.JoinerDocumentProcessor",
+ "com.yahoo.docproc.util.SplitterDocumentProcessor",
+ "com.yahoo.example.TimingSearcher",
+ "com.yahoo.language.simple.SimpleLinguistics",
+ "com.yahoo.prelude.cluster.ClusterSearcher",
+ "com.yahoo.prelude.fastsearch.FastSearcher",
+ "com.yahoo.prelude.fastsearch.VespaBackEndSearcher",
+ "com.yahoo.prelude.querytransform.CJKSearcher",
+ "com.yahoo.prelude.querytransform.CollapsePhraseSearcher",
+ "com.yahoo.prelude.querytransform.LiteralBoostSearcher",
+ "com.yahoo.prelude.querytransform.NoRankingSearcher",
+ "com.yahoo.prelude.querytransform.NonPhrasingSearcher",
+ "com.yahoo.prelude.querytransform.NormalizingSearcher",
+ "com.yahoo.prelude.querytransform.PhrasingSearcher",
+ "com.yahoo.prelude.querytransform.RecallSearcher",
+ "com.yahoo.prelude.querytransform.StemmingSearcher",
+ "com.yahoo.prelude.searcher.BlendingSearcher",
+ "com.yahoo.prelude.searcher.FieldCollapsingSearcher",
+ "com.yahoo.prelude.searcher.FillSearcher",
+ "com.yahoo.prelude.searcher.JSONDebugSearcher",
+ "com.yahoo.prelude.searcher.JuniperSearcher",
+ "com.yahoo.prelude.searcher.MultipleResultsSearcher",
+ "com.yahoo.prelude.searcher.PosSearcher",
+ "com.yahoo.prelude.searcher.QuotingSearcher",
+ "com.yahoo.prelude.searcher.ValidateSortingSearcher",
+ "com.yahoo.prelude.semantics.SemanticSearcher",
+ "com.yahoo.prelude.statistics.StatisticsSearcher",
+ "com.yahoo.prelude.templates.SearchRendererAdaptor",
+ "com.yahoo.search.Searcher",
+ "com.yahoo.search.cluster.ClusterSearcher",
+ "com.yahoo.search.cluster.PingableSearcher",
+ "com.yahoo.search.federation.FederationSearcher",
+ "com.yahoo.search.federation.ForwardingSearcher",
+ "com.yahoo.search.federation.http.ConfiguredHTTPClientSearcher",
+ "com.yahoo.search.federation.http.ConfiguredHTTPProviderSearcher",
+ "com.yahoo.search.federation.http.HTTPClientSearcher",
+ "com.yahoo.search.federation.http.HTTPProviderSearcher",
+ "com.yahoo.search.federation.http.HTTPSearcher",
+ "com.yahoo.search.federation.news.NewsSearcher",
+ "com.yahoo.search.federation.vespa.VespaSearcher",
+ "com.yahoo.search.grouping.GroupingQueryParser",
+ "com.yahoo.search.grouping.GroupingValidator",
+ "com.yahoo.search.grouping.vespa.GroupingExecutor",
+ "com.yahoo.search.handler.SearchWithRendererHandler",
+ "com.yahoo.search.pagetemplates.PageTemplate",
+ "com.yahoo.search.pagetemplates.PageTemplateSearcher",
+ "com.yahoo.search.pagetemplates.engine.Resolver",
+ "com.yahoo.search.pagetemplates.engine.resolvers.DeterministicResolver",
+ "com.yahoo.search.pagetemplates.engine.resolvers.RandomResolver",
+ "com.yahoo.search.pagetemplates.model.Renderer",
+ "com.yahoo.search.query.rewrite.QueryRewriteSearcher",
+ "com.yahoo.search.query.rewrite.SearchChainDispatcherSearcher",
+ "com.yahoo.search.query.rewrite.rewriters.GenericExpansionRewriter",
+ "com.yahoo.search.query.rewrite.rewriters.MisspellRewriter",
+ "com.yahoo.search.query.rewrite.rewriters.NameRewriter",
+ "com.yahoo.search.querytransform.AllLowercasingSearcher",
+ "com.yahoo.search.querytransform.DefaultPositionSearcher",
+ "com.yahoo.search.querytransform.LowercasingSearcher",
+ "com.yahoo.search.querytransform.NGramSearcher",
+ "com.yahoo.search.querytransform.VespaLowercasingSearcher",
+ "com.yahoo.search.rendering.Renderer",
+ "com.yahoo.search.rendering.SectionedRenderer",
+ "com.yahoo.search.searchchain.ForkingSearcher",
+ "com.yahoo.search.searchchain.example.ExampleSearcher",
+ "com.yahoo.search.searchers.CacheControlSearcher",
+ "com.yahoo.search.statistics.PeakQpsSearcher",
+ "com.yahoo.search.statistics.TimingSearcher",
+ "com.yahoo.vespa.streamingvisitors.MetricsSearcher",
+ "com.yahoo.vespa.streamingvisitors.VdsStreamingSearcher"
+ );
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java
index 6d891c55075..e7f6697aecc 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Component.java
@@ -69,4 +69,10 @@ public class Component<CHILD extends AbstractConfigProducer<?>, MODEL extends Co
return getComponentId().compareTo(other.getComponentId());
}
+ @Override
+ public String toString() {
+ return "component " + getClassId() +
+ (getClassId().toString().equals(getComponentId().toString()) ? "" : ": " + getComponentId());
+ }
+
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java
index ef2eaeb4654..fec8dd76102 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/ContainerSearch.java
@@ -11,7 +11,6 @@ import com.yahoo.vespa.configdefinition.IlscriptsConfig;
import com.yahoo.vespa.model.container.ApplicationContainerCluster;
import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.container.component.ContainerSubsystem;
-import com.yahoo.vespa.model.container.search.searchchain.LocalProvider;
import com.yahoo.vespa.model.container.search.searchchain.SearchChains;
import com.yahoo.vespa.model.search.AbstractSearchCluster;
import com.yahoo.vespa.model.search.IndexedSearchCluster;
@@ -23,7 +22,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import static com.yahoo.vespa.model.container.xml.PlatformBundles.searchAndDocprocBundle;
+import static com.yahoo.vespa.model.container.PlatformBundles.searchAndDocprocBundle;
/**
* @author gjoranv
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java
index 232e8fcbd1a..832aede858e 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/DispatcherComponent.java
@@ -5,7 +5,7 @@ import com.yahoo.config.model.producer.AbstractConfigProducer;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.vespa.config.search.DispatchConfig;
import com.yahoo.vespa.model.container.component.Component;
-import com.yahoo.vespa.model.container.xml.PlatformBundles;
+import com.yahoo.vespa.model.container.PlatformBundles;
import com.yahoo.vespa.model.search.IndexedSearchCluster;
/**
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java b/config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java
index 248b30eafa7..8c45e5b013f 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/search/RpcResourcePoolComponent.java
@@ -3,7 +3,7 @@ package com.yahoo.vespa.model.container.search;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.vespa.model.container.component.Component;
-import com.yahoo.vespa.model.container.xml.PlatformBundles;
+import com.yahoo.vespa.model.container.PlatformBundles;
public class RpcResourcePoolComponent extends Component<RpcResourcePoolComponent, ComponentModel> {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java
index 12d74418f9f..ea0ad371e28 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilder.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.model.container.xml;
import com.yahoo.config.model.builder.xml.XmlHelper;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.component.ComponentSpecification;
+import com.yahoo.vespa.model.container.PlatformBundles;
import org.w3c.dom.Element;
import java.util.Arrays;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
index 4b45979c698..c318180fd56 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
@@ -59,6 +59,7 @@ import com.yahoo.vespa.model.container.ContainerModel;
import com.yahoo.vespa.model.container.ContainerModelEvaluation;
import com.yahoo.vespa.model.container.ContainerThreadpool;
import com.yahoo.vespa.model.container.IdentityProvider;
+import com.yahoo.vespa.model.container.PlatformBundles;
import com.yahoo.vespa.model.container.SecretStore;
import com.yahoo.vespa.model.container.component.AccessLogComponent;
import com.yahoo.vespa.model.container.component.BindingPattern;
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/PlatformBundles.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/PlatformBundles.java
deleted file mode 100644
index dc2437c1834..00000000000
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/PlatformBundles.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.model.container.xml;
-
-import com.yahoo.vespa.defaults.Defaults;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Set;
-
-/**
- * @author gjoranv
- * @author Ulf Lilleengen
- */
-public class PlatformBundles {
-
- private enum JarSuffix {
- JAR_WITH_DEPS("-jar-with-dependencies.jar"),
- DEPLOY("-deploy.jar");
-
- public final String suffix;
-
- JarSuffix(String suffix) {
- this.suffix = suffix;
- }
- }
-
- public static final Path LIBRARY_PATH = Paths.get(Defaults.getDefaults().underVespaHome("lib/jars"));
- public static final String searchAndDocprocBundle = "container-search-and-docproc";
-
- private static final Set<String> searchAndDocprocComponents;
-
- public static boolean isSearchAndDocprocClass(String className) {
- return searchAndDocprocComponents.contains(className);
- }
-
- public static Path absoluteBundlePath(String fileName) {
- if (fileName == null) return null;
- return LIBRARY_PATH.resolve(Paths.get(fileName + JarSuffix.JAR_WITH_DEPS.suffix));
- }
-
- // This is a hack to allow users to declare components from the search-and-docproc bundle without naming the bundle.
- static {
- searchAndDocprocComponents = Set.of(
- "com.yahoo.docproc.AbstractConcreteDocumentFactory",
- "com.yahoo.docproc.DocumentProcessor",
- "com.yahoo.docproc.SimpleDocumentProcessor",
- "com.yahoo.docproc.util.JoinerDocumentProcessor",
- "com.yahoo.docproc.util.SplitterDocumentProcessor",
- "com.yahoo.example.TimingSearcher",
- "com.yahoo.language.simple.SimpleLinguistics",
- "com.yahoo.prelude.cluster.ClusterSearcher",
- "com.yahoo.prelude.fastsearch.FastSearcher",
- "com.yahoo.prelude.fastsearch.VespaBackEndSearcher",
- "com.yahoo.prelude.querytransform.CJKSearcher",
- "com.yahoo.prelude.querytransform.CollapsePhraseSearcher",
- "com.yahoo.prelude.querytransform.LiteralBoostSearcher",
- "com.yahoo.prelude.querytransform.NoRankingSearcher",
- "com.yahoo.prelude.querytransform.NonPhrasingSearcher",
- "com.yahoo.prelude.querytransform.NormalizingSearcher",
- "com.yahoo.prelude.querytransform.PhrasingSearcher",
- "com.yahoo.prelude.querytransform.RecallSearcher",
- "com.yahoo.prelude.querytransform.StemmingSearcher",
- "com.yahoo.prelude.searcher.BlendingSearcher",
- "com.yahoo.prelude.searcher.FieldCollapsingSearcher",
- "com.yahoo.prelude.searcher.FillSearcher",
- "com.yahoo.prelude.searcher.JSONDebugSearcher",
- "com.yahoo.prelude.searcher.JuniperSearcher",
- "com.yahoo.prelude.searcher.MultipleResultsSearcher",
- "com.yahoo.prelude.searcher.PosSearcher",
- "com.yahoo.prelude.searcher.QuotingSearcher",
- "com.yahoo.prelude.searcher.ValidateSortingSearcher",
- "com.yahoo.prelude.semantics.SemanticSearcher",
- "com.yahoo.prelude.statistics.StatisticsSearcher",
- "com.yahoo.prelude.templates.SearchRendererAdaptor",
- "com.yahoo.search.Searcher",
- "com.yahoo.search.cluster.ClusterSearcher",
- "com.yahoo.search.cluster.PingableSearcher",
- "com.yahoo.search.federation.FederationSearcher",
- "com.yahoo.search.federation.ForwardingSearcher",
- "com.yahoo.search.federation.http.ConfiguredHTTPClientSearcher",
- "com.yahoo.search.federation.http.ConfiguredHTTPProviderSearcher",
- "com.yahoo.search.federation.http.HTTPClientSearcher",
- "com.yahoo.search.federation.http.HTTPProviderSearcher",
- "com.yahoo.search.federation.http.HTTPSearcher",
- "com.yahoo.search.federation.news.NewsSearcher",
- "com.yahoo.search.federation.vespa.VespaSearcher",
- "com.yahoo.search.grouping.GroupingQueryParser",
- "com.yahoo.search.grouping.GroupingValidator",
- "com.yahoo.search.grouping.vespa.GroupingExecutor",
- "com.yahoo.search.handler.SearchWithRendererHandler",
- "com.yahoo.search.pagetemplates.PageTemplate",
- "com.yahoo.search.pagetemplates.PageTemplateSearcher",
- "com.yahoo.search.pagetemplates.engine.Resolver",
- "com.yahoo.search.pagetemplates.engine.resolvers.DeterministicResolver",
- "com.yahoo.search.pagetemplates.engine.resolvers.RandomResolver",
- "com.yahoo.search.pagetemplates.model.Renderer",
- "com.yahoo.search.query.rewrite.QueryRewriteSearcher",
- "com.yahoo.search.query.rewrite.SearchChainDispatcherSearcher",
- "com.yahoo.search.query.rewrite.rewriters.GenericExpansionRewriter",
- "com.yahoo.search.query.rewrite.rewriters.MisspellRewriter",
- "com.yahoo.search.query.rewrite.rewriters.NameRewriter",
- "com.yahoo.search.querytransform.AllLowercasingSearcher",
- "com.yahoo.search.querytransform.DefaultPositionSearcher",
- "com.yahoo.search.querytransform.LowercasingSearcher",
- "com.yahoo.search.querytransform.NGramSearcher",
- "com.yahoo.search.querytransform.VespaLowercasingSearcher",
- "com.yahoo.search.rendering.Renderer",
- "com.yahoo.search.rendering.SectionedRenderer",
- "com.yahoo.search.searchchain.ForkingSearcher",
- "com.yahoo.search.searchchain.example.ExampleSearcher",
- "com.yahoo.search.searchers.CacheControlSearcher",
- "com.yahoo.search.statistics.PeakQpsSearcher",
- "com.yahoo.search.statistics.TimingSearcher",
- "com.yahoo.vespa.streamingvisitors.MetricsSearcher",
- "com.yahoo.vespa.streamingvisitors.VdsStreamingSearcher"
- );
- }
-
-}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java b/config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java
index de5eaa2278e..7d761eb07eb 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/utils/FileSender.java
@@ -149,6 +149,7 @@ public class FileSender implements Serializable {
String path = builder.getValue();
FileReference reference = sentFiles.get(path);
if (reference == null) {
+
reference = fileRegistry.addFile(path);
send(reference, services);
sentFiles.put(path, reference);
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java
index 2c1f68e5ecc..aa068ec8f0e 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/RankProfileTestCase.java
@@ -118,13 +118,8 @@ public class RankProfileTestCase extends SchemaTestCase {
@Test
public void requireThatSidewaysInheritanceIsImpossible() throws ParseException {
- verifySidewaysInheritance(false);
- verifySidewaysInheritance(true);
- }
- private void verifySidewaysInheritance(boolean enforce) throws ParseException {
RankProfileRegistry registry = new RankProfileRegistry();
- SearchBuilder builder = new SearchBuilder(registry, setupQueryProfileTypes(),
- new TestProperties().enforceRankProfileInheritance(enforce));
+ SearchBuilder builder = new SearchBuilder(registry, setupQueryProfileTypes());
builder.importString(joinLines(
"schema child1 {",
" document child1 {",
@@ -168,15 +163,8 @@ public class RankProfileTestCase extends SchemaTestCase {
"}"));
try {
builder.build(true);
- if (enforce) {
- fail("Sideways inheritance should have been enforced");
- } else {
- assertNotNull(builder.getSearch("child2"));
- assertNotNull(builder.getSearch("child1"));
- assertTrue(registry.get("child1", "child").inherits("parent"));
- }
+ fail("Sideways inheritance should have been enforced");
} catch (IllegalArgumentException e) {
- if (!enforce) fail("Sideways inheritance should have been allowed");
assertEquals("rank-profile 'child' inherits 'parent', but it does not exist anywhere in the inheritance of search 'child1'.", e.getMessage());
}
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java
index 413daefdf75..4359e90e8a2 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerClusterTest.java
@@ -51,8 +51,7 @@ public class MetricsProxyContainerClusterTest {
var builder = new PlatformBundlesConfig.Builder();
model.getConfig(builder, CLUSTER_CONFIG_ID);
PlatformBundlesConfig config = builder.build();
- assertEquals(1, config.bundlePaths().size());
- assertThat(config.bundlePaths(0), endsWith(METRICS_PROXY_BUNDLE_FILE.toString()));
+ assertThat(config.bundlePaths(), hasItem(endsWith(METRICS_PROXY_BUNDLE_FILE.toString())));
}
@Test
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
index dd4f4c3b7d5..a66ea736a5b 100755
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
@@ -38,8 +38,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
+import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasKey;
-import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
@@ -383,10 +383,7 @@ public class ContainerClusterTest {
cluster.getConfig(bundleBuilder);
List<String> installedBundles = bundleBuilder.build().bundlePaths();
- assertEquals(expectedBundleNames.size(), installedBundles.size());
- assertThat(installedBundles, containsInAnyOrder(
- expectedBundleNames.stream().map(CoreMatchers::endsWith).collect(Collectors.toList())
- ));
+ expectedBundleNames.forEach(b -> assertThat(installedBundles, hasItem(CoreMatchers.endsWith(b))));
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java
index 70ae6a27324..4f715375e1f 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/BundleInstantiationSpecificationBuilderTest.java
@@ -5,6 +5,7 @@ import com.yahoo.component.ComponentSpecification;
import com.yahoo.config.model.builder.xml.XmlHelper;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.search.grouping.GroupingValidator;
+import com.yahoo.vespa.model.container.PlatformBundles;
import org.junit.Test;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigVerification.java
index 68a1d9b7333..56824c85413 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigVerification.java
+++ b/config-proxy/src/main/java/com/yahoo/vespa/config/proxy/ConfigVerification.java
@@ -1,5 +1,5 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.config;
+package com.yahoo.vespa.config.proxy;
import ai.vespa.util.http.hc5.VespaHttpClientBuilder;
import com.yahoo.slime.ArrayTraverser;
diff --git a/config-proxy/src/main/sh/vespa-config-verification.sh b/config-proxy/src/main/sh/vespa-config-verification.sh
index 308d7f733a4..97201d772eb 100644
--- a/config-proxy/src/main/sh/vespa-config-verification.sh
+++ b/config-proxy/src/main/sh/vespa-config-verification.sh
@@ -79,4 +79,4 @@ export ROOT
echo "# Using CLASSPATH=$CLASSPATH, args=$@"
-java -cp $CLASSPATH:$ROOT/lib/jars/config-proxy-jar-with-dependencies.jar com.yahoo.vespa.config.ConfigVerification "$@"
+java -cp $CLASSPATH:$ROOT/lib/jars/config-proxy-jar-with-dependencies.jar com.yahoo.vespa.config.proxy.ConfigVerification "$@"
diff --git a/config/src/apps/vespa-get-config/getconfig.cpp b/config/src/apps/vespa-get-config/getconfig.cpp
index 273a3abd1cd..679d118d0bc 100644
--- a/config/src/apps/vespa-get-config/getconfig.cpp
+++ b/config/src/apps/vespa-get-config/getconfig.cpp
@@ -240,7 +240,7 @@ GetConfig::Main()
printf("defNamespace %s\n", rKey.getDefNamespace().c_str());
printf("configID %s\n", rKey.getConfigId().c_str());
- printf("configMD5 %s\n", rState.md5.c_str());
+ printf("configXxhash64 %s\n", rState.xxhash64.c_str());
printf("generation %" PRId64 "\n", rState.generation);
printf("trace %s\n", response->getTrace().toString().c_str());
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java b/config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java
index 6d2ae3ef13e..7f81e937b3c 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigTransformer.java
@@ -11,9 +11,12 @@ import static com.yahoo.vespa.config.ConfigPayloadApplier.IdentityPathAcquirer;
/**
* A utility class that can be used to transform config from one format to another.
*
- * @author Ulf Lilleengen, hmusum, Tony Vaagenes
+ * @author Ulf Lilleengen
+ * @author hmusum
+ * @author Tony Vaagenes
*/
public class ConfigTransformer<T extends ConfigInstance> {
+
/**
* Workaround since FileAcquirer is in a separate module that depends on config.
* Consider moving FileAcquirer into config instead.
@@ -52,7 +55,7 @@ public class ConfigTransformer<T extends ConfigInstance> {
/**
* Create a ConfigBuilder from a payload, based on the <code>clazz</code> supplied.
*
- * @param payload a Payload to be transformed to builder.
+ * @param payload a Payload to be transformed to builder
* @return a ConfigBuilder
*/
public ConfigInstance.Builder toConfigBuilder(ConfigPayload payload) {
@@ -80,4 +83,5 @@ public class ConfigTransformer<T extends ConfigInstance> {
return builder;
}
}
+
}
diff --git a/config/src/main/java/com/yahoo/vespa/config/GenericConfig.java b/config/src/main/java/com/yahoo/vespa/config/GenericConfig.java
index 123d7c22093..bc937ef57ea 100644
--- a/config/src/main/java/com/yahoo/vespa/config/GenericConfig.java
+++ b/config/src/main/java/com/yahoo/vespa/config/GenericConfig.java
@@ -5,7 +5,6 @@ import com.yahoo.config.ConfigBuilder;
import com.yahoo.config.ConfigInstance;
/**
- *
* A generic config with an internal generic builder that mimics a real config builder in order to support builders
* when we don't have the schema.
*
diff --git a/config/src/tests/configagent/configagent.cpp b/config/src/tests/configagent/configagent.cpp
index d6766cce822..e4dc94fc2de 100644
--- a/config/src/tests/configagent/configagent.cpp
+++ b/config/src/tests/configagent/configagent.cpp
@@ -31,12 +31,12 @@ class MyConfigResponse : public ConfigResponse
{
public:
MyConfigResponse(const ConfigKey & key, const ConfigValue & value, bool valid, int64_t timestamp,
- const vespalib::string & md5, const std::string & errorMsg, int errorC0de, bool iserror)
+ const vespalib::string & xxhash64, const std::string & errorMsg, int errorC0de, bool iserror)
: _key(key),
_value(value),
_fillCalled(false),
_valid(valid),
- _state(md5, timestamp, false),
+ _state(xxhash64, timestamp, false),
_errorMessage(errorMsg),
_errorCode(errorC0de),
_isError(iserror)
@@ -64,9 +64,9 @@ public:
Trace _trace;
- static ConfigResponse::UP createOKResponse(const ConfigKey & key, const ConfigValue & value, uint64_t timestamp = 10, const vespalib::string & md5 = "a")
+ static ConfigResponse::UP createOKResponse(const ConfigKey & key, const ConfigValue & value, uint64_t timestamp = 10, const vespalib::string & xxhash64 = "a")
{
- return std::make_unique<MyConfigResponse>(key, value, true, timestamp, md5, "", 0, false);
+ return std::make_unique<MyConfigResponse>(key, value, true, timestamp, xxhash64, "", 0, false);
}
static ConfigResponse::UP createServerErrorResponse(const ConfigKey & key, const ConfigValue & value)
@@ -114,11 +114,11 @@ private:
};
-ConfigValue createValue(const std::string & myField, const std::string & md5)
+ConfigValue createValue(const std::string & myField, const std::string & xxhash64)
{
std::vector< vespalib::string > lines;
lines.push_back("myField \"" + myField + "\"");
- return ConfigValue(lines, md5);
+ return ConfigValue(lines, xxhash64);
}
static TimingValues testTimingValues(
@@ -139,7 +139,7 @@ TEST("require that agent returns correct values") {
ASSERT_EQUAL(500u, handler.getTimeout());
ASSERT_EQUAL(0u, handler.getWaitTime());
ConfigState cs;
- ASSERT_EQUAL(cs.md5, handler.getConfigState().md5);
+ ASSERT_EQUAL(cs.xxhash64, handler.getConfigState().xxhash64);
ASSERT_EQUAL(cs.generation, handler.getConfigState().generation);
ASSERT_EQUAL(cs.applyOnRestart, handler.getConfigState().applyOnRestart);
}
@@ -167,7 +167,7 @@ TEST("require that important(the change) request is delivered to holder even if
FRTConfigAgent handler(latch, testTimingValues);
handler.handleResponse(MyConfigRequest(testKey),
- MyConfigResponse::createOKResponse(testKey, testValue1, 1, testValue1.getMd5()));
+ MyConfigResponse::createOKResponse(testKey, testValue1, 1, testValue1.getXxhash64()));
ASSERT_TRUE(latch->poll());
ConfigUpdate::UP update(latch->provide());
ASSERT_TRUE(update);
@@ -176,9 +176,9 @@ TEST("require that important(the change) request is delivered to holder even if
ASSERT_EQUAL("l33t", cfg.myField);
handler.handleResponse(MyConfigRequest(testKey),
- MyConfigResponse::createOKResponse(testKey, testValue2, 2, testValue2.getMd5()));
+ MyConfigResponse::createOKResponse(testKey, testValue2, 2, testValue2.getXxhash64()));
handler.handleResponse(MyConfigRequest(testKey),
- MyConfigResponse::createOKResponse(testKey, testValue2, 3, testValue2.getMd5()));
+ MyConfigResponse::createOKResponse(testKey, testValue2, 3, testValue2.getXxhash64()));
ASSERT_TRUE(latch->poll());
update = latch->provide();
ASSERT_TRUE(update);
diff --git a/config/src/tests/failover/failover.cpp b/config/src/tests/failover/failover.cpp
index 2e039081716..cb8b7121b02 100644
--- a/config/src/tests/failover/failover.cpp
+++ b/config/src/tests/failover/failover.cpp
@@ -60,7 +60,7 @@ struct RPCServer : public FRT_Invokable {
info.setString("uncompressedSize", "0");
root.setString(RESPONSE_CONFIGID, "myId");
root.setString(RESPONSE_CLIENT_HOSTNAME, "myhost");
- root.setString(RESPONSE_CONFIG_MD5, "md5");
+ root.setString(RESPONSE_CONFIG_XXHASH64, "xxhash64");
root.setLong(RESPONSE_CONFIG_GENERATION, gen);
root.setObject(RESPONSE_TRACE);
Slime payload;
diff --git a/config/src/tests/frt/frt.cpp b/config/src/tests/frt/frt.cpp
index cb09b8f7254..5af02f22ed2 100644
--- a/config/src/tests/frt/frt.cpp
+++ b/config/src/tests/frt/frt.cpp
@@ -72,7 +72,7 @@ namespace {
FRT_RPCRequest * createOKResponse(const vespalib::string & defName="",
const vespalib::string & defMd5="",
const vespalib::string & configId="",
- const vespalib::string & configMd5="",
+ const vespalib::string & configXxhash64="",
int changed=0,
long generation=0,
const std::vector<vespalib::string> & payload = std::vector<vespalib::string>(),
@@ -85,7 +85,7 @@ namespace {
ret.AddString("");
ret.AddString(defMd5.c_str());
ret.AddString(configId.c_str());
- ret.AddString(configMd5.c_str());
+ ret.AddString(configXxhash64.c_str());
ret.AddInt32(changed);
ret.AddInt64(generation);
FRT_StringValue * payload_arr = ret.AddStringArray(payload.size());
@@ -268,16 +268,16 @@ TEST_FF("require that request is config task is scheduled", SourceFixture(), FRT
TEST("require that v3 request is correctly initialized") {
ConnectionMock conn;
ConfigKey key = ConfigKey::create<MyConfig>("foobi");
- vespalib::string md5 = "mymd5";
+ vespalib::string xxhash64 = "myxxhash64";
int64_t currentGeneration = 3;
vespalib::string hostName = "myhost";
int64_t timeout = 3000;
Trace traceIn(3);
traceIn.trace(2, "Hei");
- FRTConfigRequestV3 v3req(&conn, key, md5, currentGeneration, hostName,
+ FRTConfigRequestV3 v3req(&conn, key, xxhash64, currentGeneration, hostName,
timeout, traceIn, VespaVersion::fromString("1.2.3"), CompressionType::LZ4);
- ASSERT_TRUE(v3req.verifyState(ConfigState(md5, 3, false)));
- ASSERT_FALSE(v3req.verifyState(ConfigState(md5, 2, false)));
+ ASSERT_TRUE(v3req.verifyState(ConfigState(xxhash64, 3, false)));
+ ASSERT_FALSE(v3req.verifyState(ConfigState(xxhash64, 2, false)));
ASSERT_FALSE(v3req.verifyState(ConfigState("xxx", 3, false)));
ASSERT_FALSE(v3req.verifyState(ConfigState("xxx", 2, false)));
@@ -297,7 +297,7 @@ TEST("require that v3 request is correctly initialized") {
EXPECT_EQUAL(key.getConfigId(), root[REQUEST_CLIENT_CONFIGID].asString().make_string());
EXPECT_EQUAL(hostName, root[REQUEST_CLIENT_HOSTNAME].asString().make_string());
EXPECT_EQUAL(currentGeneration, root[REQUEST_CURRENT_GENERATION].asLong());
- EXPECT_EQUAL(md5, root[REQUEST_CONFIG_MD5].asString().make_string());
+ EXPECT_EQUAL(xxhash64, root[REQUEST_CONFIG_XXHASH64].asString().make_string());
EXPECT_EQUAL(timeout, root[REQUEST_TIMEOUT].asLong());
EXPECT_EQUAL("LZ4", root[REQUEST_COMPRESSION_TYPE].asString().make_string());
EXPECT_EQUAL(root[REQUEST_VESPA_VERSION].asString().make_string(), "1.2.3");
@@ -322,7 +322,7 @@ struct V3RequestFixture {
Cursor & root;
FRT_RPCRequest * req;
ConfigKey key;
- vespalib::string md5;
+ vespalib::string xxhash64;
int64_t generation;
vespalib::string hostname;
Trace traceIn;
@@ -333,7 +333,7 @@ struct V3RequestFixture {
root(slime.setObject()),
req(conn.allocRPCRequest()),
key(ConfigKey::create<BarConfig>("foobi")),
- md5("mymd5"),
+ xxhash64("myxxhash64"),
generation(3),
hostname("myhhost"),
traceIn(3)
@@ -345,7 +345,7 @@ struct V3RequestFixture {
root.setString(RESPONSE_DEF_MD5, Memory(key.getDefMd5()));
root.setString(RESPONSE_CONFIGID, Memory(key.getConfigId()));
root.setString(RESPONSE_CLIENT_HOSTNAME, Memory(hostname));
- root.setString(RESPONSE_CONFIG_MD5, Memory(md5));
+ root.setString(RESPONSE_CONFIG_XXHASH64, Memory(xxhash64));
root.setLong(RESPONSE_CONFIG_GENERATION, generation);
traceIn.serialize(root.setObject(RESPONSE_TRACE));
}
@@ -379,7 +379,7 @@ struct V3RequestFixture {
EXPECT_EQUAL(key.getConfigId(), responseKey.getConfigId());
EXPECT_EQUAL(hostname, response.getHostName());
ConfigState state(response.getConfigState());
- EXPECT_EQUAL(md5, state.md5);
+ EXPECT_EQUAL(xxhash64, state.xxhash64);
EXPECT_EQUAL(generation, state.generation);
ConfigValue value(response.getValue());
BarConfig::UP config(value.newInstance<BarConfig>());
diff --git a/config/src/tests/misc/misc.cpp b/config/src/tests/misc/misc.cpp
index 1a8b8a59ede..55040242732 100644
--- a/config/src/tests/misc/misc.cpp
+++ b/config/src/tests/misc/misc.cpp
@@ -15,11 +15,11 @@ TEST("requireThatConfigUpdateWorks") {
std::vector<vespalib::string> lines;
lines.push_back("foo");
- ConfigUpdate up(ConfigValue(lines, "mymd5"), true, 1337);
+ ConfigUpdate up(ConfigValue(lines, "myxxhash"), true, 1337);
ASSERT_EQUAL(1337, up.getGeneration());
ASSERT_TRUE(up.hasChanged());
- ConfigUpdate up2(ConfigValue(lines, "mymd52"), false, 1338);
+ ConfigUpdate up2(ConfigValue(lines, "myxxhash2"), false, 1338);
ASSERT_EQUAL(1338, up2.getGeneration());
ASSERT_FALSE(up2.hasChanged());
}
@@ -27,22 +27,22 @@ TEST("requireThatConfigUpdateWorks") {
TEST("requireThatConfigValueWorks") {
std::vector<vespalib::string> lines;
lines.push_back("myFooField \"bar\"");
- ConfigValue v1(lines, calculateContentMd5(lines));
- ConfigValue v2(lines, calculateContentMd5(lines));
- ConfigValue v3(lines, calculateContentMd5(lines));
+ ConfigValue v1(lines, calculateContentXxhash64(lines));
+ ConfigValue v2(lines, calculateContentXxhash64(lines));
+ ConfigValue v3(lines, calculateContentXxhash64(lines));
lines.push_back("myFooField \"bar2\"");
- ConfigValue v4(lines, calculateContentMd5(lines));
+ ConfigValue v4(lines, calculateContentXxhash64(lines));
ASSERT_TRUE(v1 == v2);
ASSERT_TRUE(v1 == v3);
}
TEST("requireThatConfigKeyWorks") {
- ConfigKey key1("id1", "def1", "namespace1", "md51");
- ConfigKey key2("id1", "def1", "namespace1", "md51");
- ConfigKey key3("id2", "def1", "namespace1", "md51");
- ConfigKey key4("id1", "def2", "namespace1", "md51");
- ConfigKey key5("id1", "def1", "namespace2", "md51");
- ConfigKey key6("id1", "def1", "namespace1", "md52"); // Special case. Md5 does not matter, so should be qual to key1 and key2
+ ConfigKey key1("id1", "def1", "namespace1", "xxhash1");
+ ConfigKey key2("id1", "def1", "namespace1", "xxhash1");
+ ConfigKey key3("id2", "def1", "namespace1", "xxhash1");
+ ConfigKey key4("id1", "def2", "namespace1", "xxhash1");
+ ConfigKey key5("id1", "def1", "namespace2", "xxhash1");
+ ConfigKey key6("id1", "def1", "namespace1", "xxhash2"); // Special case. xxhash64 does not matter, so should be qual to key1 and key2
ASSERT_TRUE(key1 == key2);
@@ -111,7 +111,7 @@ TEST("require that config key initializes schema")
std::vector<vespalib::string> schema;
schema.push_back("foo");
schema.push_back("bar");
- ConfigKey key("id1", "def1", "namespace1", "md51", schema);
+ ConfigKey key("id1", "def1", "namespace1", "xxhash1", schema);
const std::vector<vespalib::string> &vref(key.getDefSchema());
for (size_t i = 0; i < schema.size(); i++) {
ASSERT_EQUAL(schema[i], vref[i]);
@@ -131,6 +131,7 @@ TEST("require that error codes are correctly translated to strings") {
ASSERT_CONFIG(ILLEGAL_CONFIGID);
ASSERT_CONFIG(ILLEGAL_DEF_MD5);
ASSERT_CONFIG(ILLEGAL_CONFIG_MD5);
+ ASSERT_CONFIG(ILLEGAL_CONFIG_MD5);
ASSERT_CONFIG(ILLEGAL_TIMEOUT);
ASSERT_CONFIG(ILLEGAL_TIMESTAMP);
ASSERT_CONFIG(ILLEGAL_NAME_SPACE);
diff --git a/config/src/tests/subscriber/subscriber.cpp b/config/src/tests/subscriber/subscriber.cpp
index d58699f26e0..5c5714a867a 100644
--- a/config/src/tests/subscriber/subscriber.cpp
+++ b/config/src/tests/subscriber/subscriber.cpp
@@ -20,7 +20,7 @@ namespace {
{
std::vector< vespalib::string > lines;
lines.push_back(value);
- return ConfigValue(lines, calculateContentMd5(lines));
+ return ConfigValue(lines, calculateContentXxhash64(lines));
}
ConfigValue createFooValue(const std::string & value)
diff --git a/config/src/vespa/config/common/configstate.h b/config/src/vespa/config/common/configstate.h
index 2dbea3cc30f..143d77e4ab9 100644
--- a/config/src/vespa/config/common/configstate.h
+++ b/config/src/vespa/config/common/configstate.h
@@ -13,17 +13,17 @@ struct ConfigState
{
public:
ConfigState()
- : md5(""),
+ : xxhash64(""),
generation(0),
applyOnRestart(false)
{ }
- ConfigState(const vespalib::string & md5sum, int64_t gen, bool _applyOnRestart)
- : md5(md5sum),
+ ConfigState(const vespalib::string & xxhash, int64_t gen, bool _applyOnRestart)
+ : xxhash64(xxhash),
generation(gen),
applyOnRestart(_applyOnRestart)
{ }
- vespalib::string md5;
+ vespalib::string xxhash64;
int64_t generation;
bool applyOnRestart;
@@ -32,7 +32,7 @@ public:
}
bool hasDifferentPayloadFrom(const ConfigState & other) const {
- return (md5.compare(other.md5) != 0);
+ return (xxhash64.compare(other.xxhash64) != 0);
}
};
diff --git a/config/src/vespa/config/common/configvalue.cpp b/config/src/vespa/config/common/configvalue.cpp
index d5c0c2047df..ce6fd3f20da 100644
--- a/config/src/vespa/config/common/configvalue.cpp
+++ b/config/src/vespa/config/common/configvalue.cpp
@@ -6,22 +6,22 @@
namespace config {
-ConfigValue::ConfigValue(const std::vector<vespalib::string> & lines, const vespalib::string & md5sum)
+ConfigValue::ConfigValue(const std::vector<vespalib::string> & lines, const vespalib::string & xxhash)
: _payload(),
_lines(lines),
- _md5sum(md5sum)
+ _xxhash64(xxhash)
{ }
ConfigValue::ConfigValue()
: _payload(),
_lines(),
- _md5sum()
+ _xxhash64()
{ }
-ConfigValue::ConfigValue(PayloadPtr payload, const vespalib::string & md5)
+ConfigValue::ConfigValue(PayloadPtr payload, const vespalib::string & xxhash)
: _payload(std::move(payload)),
_lines(),
- _md5sum(md5)
+ _xxhash64(xxhash)
{ }
ConfigValue::ConfigValue(const ConfigValue &) = default;
@@ -32,7 +32,7 @@ ConfigValue::~ConfigValue() = default;
int
ConfigValue::operator==(const ConfigValue & rhs) const
{
- return (_md5sum.compare(rhs._md5sum) == 0);
+ return (_xxhash64.compare(rhs._xxhash64) == 0);
}
int
diff --git a/config/src/vespa/config/common/configvalue.h b/config/src/vespa/config/common/configvalue.h
index a0450328f30..f5bfae00c19 100644
--- a/config/src/vespa/config/common/configvalue.h
+++ b/config/src/vespa/config/common/configvalue.h
@@ -21,8 +21,8 @@ typedef std::shared_ptr<const protocol::Payload> PayloadPtr;
class ConfigValue {
public:
typedef std::unique_ptr<ConfigValue> UP;
- ConfigValue(const std::vector<vespalib::string> & lines, const vespalib::string & md5sum);
- ConfigValue(PayloadPtr data, const vespalib::string & md5sum);
+ ConfigValue(const std::vector<vespalib::string> & lines, const vespalib::string & xxhash);
+ ConfigValue(PayloadPtr data, const vespalib::string & xxhash);
ConfigValue();
ConfigValue(const ConfigValue &);
ConfigValue & operator = (const ConfigValue &);
@@ -36,7 +36,7 @@ public:
const std::vector<vespalib::string> & getLines() const { return _lines; }
std::vector<vespalib::string> getLegacyFormat() const;
const vespalib::string asJson() const;
- const vespalib::string getMd5() const { return _md5sum; }
+ const vespalib::string getXxhash64() const { return _xxhash64; }
void serializeV1(::vespalib::slime::Cursor & cursor) const;
void serializeV2(::vespalib::slime::Cursor & cursor) const;
@@ -47,7 +47,7 @@ public:
private:
PayloadPtr _payload;
std::vector<vespalib::string> _lines;
- vespalib::string _md5sum;
+ vespalib::string _xxhash64;
};
} //namespace config
diff --git a/config/src/vespa/config/common/misc.cpp b/config/src/vespa/config/common/misc.cpp
index 1040962e25c..e1b4390caf5 100644
--- a/config/src/vespa/config/common/misc.cpp
+++ b/config/src/vespa/config/common/misc.cpp
@@ -1,7 +1,9 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "misc.h"
-#include <vespa/vespalib/util/md5.h>
+#include <iostream>
+#include <sstream>
+#include <xxhash.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/data/slime/slime.h>
@@ -12,12 +14,12 @@ using vespalib::Memory;
namespace config {
vespalib::string
-calculateContentMd5(const std::vector<vespalib::string> & fileContents)
+calculateContentXxhash64(const std::vector<vespalib::string> & fileContents)
{
vespalib::string normalizedLines;
- int compact_md5size = 16;
- unsigned char md5sum[compact_md5size];
+ XXH64_hash_t xxhash64;
vespalib::asciistream s;
+ std::stringstream ss;
// remove comments, trailing spaces and empty lines
// TODO: Remove multiple spaces and space before comma, like in Java
@@ -30,16 +32,11 @@ calculateContentMd5(const std::vector<vespalib::string> & fileContents)
normalizedLines += line;
}
}
- fastc_md5sum((const unsigned char*)normalizedLines.c_str(), normalizedLines.size(), md5sum);
+ xxhash64 = XXH64((const unsigned char*)normalizedLines.c_str(), normalizedLines.size(), 0);
- // convert to 32 character hex string
- for (int i = 0; i < compact_md5size; i++) {
- if (md5sum[i] < 16) {
- s << "0";
- }
- s << vespalib::hex << (int)md5sum[i];
- }
- return s.str();
+ ss << std::hex << xxhash64;
+ ss << std::endl;
+ return ss.str();
}
bool
diff --git a/config/src/vespa/config/common/misc.h b/config/src/vespa/config/common/misc.h
index 0299ef001f1..089fd890224 100644
--- a/config/src/vespa/config/common/misc.h
+++ b/config/src/vespa/config/common/misc.h
@@ -19,7 +19,7 @@ namespace config {
/**
* Miscellaneous utility functions specific to config.
*/
-vespalib::string calculateContentMd5(const std::vector<vespalib::string> & fileContents);
+vespalib::string calculateContentXxhash64(const std::vector<vespalib::string> & fileContents);
bool isGenerationNewer(int64_t newGen, int64_t oldGen);
diff --git a/config/src/vespa/config/file/filesource.cpp b/config/src/vespa/config/file/filesource.cpp
index fcc69f68066..8e25d5ccffc 100644
--- a/config/src/vespa/config/file/filesource.cpp
+++ b/config/src/vespa/config/file/filesource.cpp
@@ -27,10 +27,10 @@ FileSource::getConfig()
int64_t last = getLast(_fileName);
if (last > _lastLoaded) {
- _holder->handle(ConfigUpdate::UP(new ConfigUpdate(ConfigValue(lines, calculateContentMd5(lines)), true, _generation)));
+ _holder->handle(ConfigUpdate::UP(new ConfigUpdate(ConfigValue(lines, calculateContentXxhash64(lines)), true, _generation)));
_lastLoaded = last;
} else {
- _holder->handle(ConfigUpdate::UP(new ConfigUpdate(ConfigValue(lines, calculateContentMd5(lines)), false, _generation)));
+ _holder->handle(ConfigUpdate::UP(new ConfigUpdate(ConfigValue(lines, calculateContentXxhash64(lines)), false, _generation)));
}
}
diff --git a/config/src/vespa/config/frt/frtconfigagent.cpp b/config/src/vespa/config/frt/frtconfigagent.cpp
index 0e5ee4f6b05..30112b677e1 100644
--- a/config/src/vespa/config/frt/frtconfigagent.cpp
+++ b/config/src/vespa/config/frt/frtconfigagent.cpp
@@ -27,7 +27,7 @@ FRTConfigAgent::handleResponse(const ConfigRequest & request, ConfigResponse::UP
{
if (LOG_WOULD_LOG(spam)) {
const ConfigKey & key(request.getKey());
- LOG(spam, "current state for %s: generation %" PRId64 " md5 %s", key.toString().c_str(), _configState.generation, _configState.md5.c_str());
+ LOG(spam, "current state for %s: generation %" PRId64 " xxhash64 %s", key.toString().c_str(), _configState.generation, _configState.xxhash64.c_str());
}
if (response->validateResponse() && !response->isError()) {
handleOKResponse(request, std::move(response));
@@ -57,12 +57,12 @@ void
FRTConfigAgent::handleUpdatedGeneration(const ConfigKey & key, const ConfigState & newState, const ConfigValue & configValue)
{
if (LOG_WOULD_LOG(spam)) {
- LOG(spam, "new generation %" PRId64 " md5:%s for key %s", newState.generation, newState.md5.c_str(), key.toString().c_str());
- LOG(spam, "Old config: md5:%s \n%s", _latest.getMd5().c_str(), _latest.asJson().c_str());
- LOG(spam, "New config: md5:%s \n%s", configValue.getMd5().c_str(), configValue.asJson().c_str());
+ LOG(spam, "new generation %" PRId64 " xxhash64:%s for key %s", newState.generation, newState.xxhash64.c_str(), key.toString().c_str());
+ LOG(spam, "Old config: xxhash64:%s \n%s", _latest.getXxhash64().c_str(), _latest.asJson().c_str());
+ LOG(spam, "New config: xxhash64:%s \n%s", configValue.getXxhash64().c_str(), configValue.asJson().c_str());
}
bool changed = false;
- if (_latest.getMd5() != configValue.getMd5()) {
+ if (_latest.getXxhash64() != configValue.getXxhash64()) {
_latest = configValue;
changed = true;
}
diff --git a/config/src/vespa/config/frt/frtconfigrequestfactory.cpp b/config/src/vespa/config/frt/frtconfigrequestfactory.cpp
index fbc13556d14..3573b8c9cfe 100644
--- a/config/src/vespa/config/frt/frtconfigrequestfactory.cpp
+++ b/config/src/vespa/config/frt/frtconfigrequestfactory.cpp
@@ -24,7 +24,7 @@ FRTConfigRequest::UP
FRTConfigRequestFactory::createConfigRequest(const ConfigKey & key, Connection * connection,
const ConfigState & state, int64_t serverTimeout) const
{
- return make_unique<FRTConfigRequestV3>(connection, key, state.md5, state.generation, _hostName,
+ return make_unique<FRTConfigRequestV3>(connection, key, state.xxhash64, state.generation, _hostName,
serverTimeout, Trace(_traceLevel), _vespaVersion, _compressionType);
}
diff --git a/config/src/vespa/config/frt/frtconfigresponsev3.cpp b/config/src/vespa/config/frt/frtconfigresponsev3.cpp
index 80cdf88a79a..351f0fc8136 100644
--- a/config/src/vespa/config/frt/frtconfigresponsev3.cpp
+++ b/config/src/vespa/config/frt/frtconfigresponsev3.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "frtconfigresponsev3.h"
#include "compressioninfo.h"
#include <vespa/fnet/frt/values.h>
@@ -53,7 +53,7 @@ FRTConfigResponseV3::getResponseTypes() const
ConfigValue
FRTConfigResponseV3::readConfigValue() const
{
- vespalib::string md5(_data->get()[RESPONSE_CONFIG_MD5].asString().make_string());
+ vespalib::string xxhash64(_data->get()[RESPONSE_CONFIG_XXHASH64].asString().make_string());
CompressionInfo info;
info.deserialize(_data->get()[RESPONSE_COMPRESSION_INFO]);
auto slime = std::make_unique<Slime>();
@@ -67,9 +67,9 @@ FRTConfigResponseV3::readConfigValue() const
}
}
if (LOG_WOULD_LOG(spam)) {
- LOG(spam, "read config value md5(%s), payload size: %lu", md5.c_str(), data.memRef.size);
+ LOG(spam, "read config value xxhash64(%s), payload size: %lu", xxhash64.c_str(), data.memRef.size);
}
- return ConfigValue(std::make_shared<V3Payload>(std::move(slime)), md5);
+ return ConfigValue(std::make_shared<V3Payload>(std::move(slime)), xxhash64);
}
} // namespace config
diff --git a/config/src/vespa/config/frt/protocol.cpp b/config/src/vespa/config/frt/protocol.cpp
index 4a1b0a6ddef..c6c99ccf1ff 100644
--- a/config/src/vespa/config/frt/protocol.cpp
+++ b/config/src/vespa/config/frt/protocol.cpp
@@ -25,6 +25,7 @@ const Memory REQUEST_DEF_CONTENT = "defContent";
const Memory REQUEST_CLIENT_CONFIGID = "configId";
const Memory REQUEST_CLIENT_HOSTNAME = "clientHostname";
const Memory REQUEST_CONFIG_MD5 = "configMD5";
+const Memory REQUEST_CONFIG_XXHASH64 = "configXxhash64";
const Memory REQUEST_CURRENT_GENERATION = "currentGeneration";
const Memory REQUEST_TIMEOUT = "timeout";
const Memory REQUEST_TRACE = "trace";
@@ -36,7 +37,7 @@ const Memory RESPONSE_DEF_NAMESPACE = "defNamespace";
const Memory RESPONSE_DEF_MD5 = "defMD5";
const Memory RESPONSE_CONFIGID = "configId";
const Memory RESPONSE_CLIENT_HOSTNAME = "clientHostname";
-const Memory RESPONSE_CONFIG_MD5 = "configMD5";
+const Memory RESPONSE_CONFIG_XXHASH64 = "configXxhash64";
const Memory RESPONSE_CONFIG_GENERATION = "generation";
const Memory RESPONSE_PAYLOAD = "payload";
const Memory RESPONSE_TRACE = "trace";
diff --git a/config/src/vespa/config/frt/protocol.h b/config/src/vespa/config/frt/protocol.h
index 0ec16952701..ce09217b619 100644
--- a/config/src/vespa/config/frt/protocol.h
+++ b/config/src/vespa/config/frt/protocol.h
@@ -36,7 +36,7 @@ extern const vespalib::Memory REQUEST_DEF_MD5;
extern const vespalib::Memory REQUEST_DEF_CONTENT;
extern const vespalib::Memory REQUEST_CLIENT_CONFIGID;
extern const vespalib::Memory REQUEST_CLIENT_HOSTNAME;
-extern const vespalib::Memory REQUEST_CONFIG_MD5;
+extern const vespalib::Memory REQUEST_CONFIG_XXHASH64;
extern const vespalib::Memory REQUEST_CURRENT_GENERATION;
extern const vespalib::Memory REQUEST_TIMEOUT;
extern const vespalib::Memory REQUEST_TRACE;
@@ -48,7 +48,7 @@ extern const vespalib::Memory RESPONSE_DEF_NAMESPACE;
extern const vespalib::Memory RESPONSE_DEF_MD5;
extern const vespalib::Memory RESPONSE_CONFIGID;
extern const vespalib::Memory RESPONSE_CLIENT_HOSTNAME;
-extern const vespalib::Memory RESPONSE_CONFIG_MD5;
+extern const vespalib::Memory RESPONSE_CONFIG_XXHASH64;
extern const vespalib::Memory RESPONSE_CONFIG_GENERATION;
extern const vespalib::Memory RESPONSE_PAYLOAD;
extern const vespalib::Memory RESPONSE_TRACE;
diff --git a/config/src/vespa/config/frt/slimeconfigrequest.cpp b/config/src/vespa/config/frt/slimeconfigrequest.cpp
index 8a6706974f6..3573f8c07b9 100644
--- a/config/src/vespa/config/frt/slimeconfigrequest.cpp
+++ b/config/src/vespa/config/frt/slimeconfigrequest.cpp
@@ -23,7 +23,7 @@ const vespalib::string SlimeConfigRequest::REQUEST_TYPES = "s";
SlimeConfigRequest::SlimeConfigRequest(Connection * connection,
const ConfigKey & key,
- const vespalib::string & configMd5,
+ const vespalib::string & configXxhash64,
int64_t currentGeneration,
const vespalib::string & hostName,
int64_t serverTimeout,
@@ -35,7 +35,7 @@ SlimeConfigRequest::SlimeConfigRequest(Connection * connection,
: FRTConfigRequest(connection, key),
_data()
{
- populateSlimeRequest(key, configMd5, currentGeneration, hostName, serverTimeout, trace, vespaVersion, protocolVersion, compressionType);
+ populateSlimeRequest(key, configXxhash64, currentGeneration, hostName, serverTimeout, trace, vespaVersion, protocolVersion, compressionType);
_request->SetMethodName(methodName.c_str());
_parameters.AddString(createJsonFromSlime(_data).c_str());
}
@@ -43,13 +43,13 @@ SlimeConfigRequest::SlimeConfigRequest(Connection * connection,
bool
SlimeConfigRequest::verifyState(const ConfigState & state) const
{
- return (state.md5.compare(_data[REQUEST_CONFIG_MD5].asString().make_stringref()) == 0 &&
+ return (state.xxhash64.compare(_data[REQUEST_CONFIG_XXHASH64].asString().make_stringref()) == 0 &&
state.generation == _data[REQUEST_CURRENT_GENERATION].asLong());
}
void
SlimeConfigRequest::populateSlimeRequest(const ConfigKey & key,
- const vespalib::string & configMd5,
+ const vespalib::string & configXxhash64,
int64_t currentGeneration,
const vespalib::string & hostName,
int64_t serverTimeout,
@@ -67,7 +67,7 @@ SlimeConfigRequest::populateSlimeRequest(const ConfigKey & key,
def.serialize(root.setArray(REQUEST_DEF_CONTENT));
root.setString(REQUEST_CLIENT_CONFIGID, Memory(key.getConfigId()));
root.setString(REQUEST_CLIENT_HOSTNAME, Memory(hostName));
- root.setString(REQUEST_CONFIG_MD5, Memory(configMd5));
+ root.setString(REQUEST_CONFIG_XXHASH64, Memory(configXxhash64));
root.setLong(REQUEST_CURRENT_GENERATION, currentGeneration);
root.setLong(REQUEST_TIMEOUT, serverTimeout);
trace.serialize(root.setObject(REQUEST_TRACE));
diff --git a/config/src/vespa/config/frt/slimeconfigrequest.h b/config/src/vespa/config/frt/slimeconfigrequest.h
index 6f2f42c98d5..d25530f815c 100644
--- a/config/src/vespa/config/frt/slimeconfigrequest.h
+++ b/config/src/vespa/config/frt/slimeconfigrequest.h
@@ -19,7 +19,7 @@ class SlimeConfigRequest : public FRTConfigRequest {
public:
SlimeConfigRequest(Connection * connection,
const ConfigKey & key,
- const vespalib::string & configMd5,
+ const vespalib::string & configXxhash64,
int64_t currentGeneration,
const vespalib::string & hostName,
int64_t serverTimeout,
@@ -33,7 +33,7 @@ public:
virtual ConfigResponse::UP createResponse(FRT_RPCRequest * request) const override = 0;
private:
void populateSlimeRequest(const ConfigKey & key,
- const vespalib::string & configMd5,
+ const vespalib::string & configXxhash64,
int64_t currentGeneration,
const vespalib::string & hostName,
int64_t serverTimeout,
diff --git a/config/src/vespa/config/frt/slimeconfigresponse.cpp b/config/src/vespa/config/frt/slimeconfigresponse.cpp
index af224008d01..c1f3df7f674 100644
--- a/config/src/vespa/config/frt/slimeconfigresponse.cpp
+++ b/config/src/vespa/config/frt/slimeconfigresponse.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "slimeconfigresponse.h"
#include <vespa/config/common/misc.h>
#include <vespa/fnet/frt/values.h>
@@ -66,7 +66,7 @@ ConfigState
SlimeConfigResponse::readState() const
{
const Slime & data(*_data);
- return ConfigState(data.get()[RESPONSE_CONFIG_MD5].asString().make_string(),
+ return ConfigState(data.get()[RESPONSE_CONFIG_XXHASH64].asString().make_string(),
data.get()[RESPONSE_CONFIG_GENERATION].asLong(),
data.get()[RESPONSE_APPLY_ON_RESTART].asBool());
}
diff --git a/config/src/vespa/config/print/asciiconfigreader.hpp b/config/src/vespa/config/print/asciiconfigreader.hpp
index b4a25f4fbbd..9d142e83c00 100644
--- a/config/src/vespa/config/print/asciiconfigreader.hpp
+++ b/config/src/vespa/config/print/asciiconfigreader.hpp
@@ -27,7 +27,7 @@ AsciiConfigReader<ConfigType>::read()
while (getline(_is, line)) {
lines.push_back(line);
}
- return std::unique_ptr<ConfigType>(new ConfigType(ConfigValue(lines, calculateContentMd5(lines))));
+ return std::unique_ptr<ConfigType>(new ConfigType(ConfigValue(lines, calculateContentXxhash64(lines))));
}
} // namespace config
diff --git a/config/src/vespa/config/print/fileconfigreader.hpp b/config/src/vespa/config/print/fileconfigreader.hpp
index bfebb4b89b9..87a4e20866c 100644
--- a/config/src/vespa/config/print/fileconfigreader.hpp
+++ b/config/src/vespa/config/print/fileconfigreader.hpp
@@ -44,7 +44,7 @@ FileConfigReader<ConfigType>::read()
for (std::getline(f, line); f; std::getline(f, line)) {
lines.push_back(line);
}
- return std::unique_ptr<ConfigType>(new ConfigType(ConfigValue(lines, calculateContentMd5(lines))));
+ return std::unique_ptr<ConfigType>(new ConfigType(ConfigValue(lines, calculateContentXxhash64(lines))));
}
} // namespace config
diff --git a/config/src/vespa/config/print/istreamconfigreader.hpp b/config/src/vespa/config/print/istreamconfigreader.hpp
index 70f7b7e5f28..3a501d26245 100644
--- a/config/src/vespa/config/print/istreamconfigreader.hpp
+++ b/config/src/vespa/config/print/istreamconfigreader.hpp
@@ -31,7 +31,7 @@ IstreamConfigReader<ConfigType>::read()
while (getline(_is, line)) {
lines.push_back(line);
}
- return std::unique_ptr<ConfigType>(new ConfigType(ConfigValue(lines, calculateContentMd5(lines))));
+ return std::unique_ptr<ConfigType>(new ConfigType(ConfigValue(lines, calculateContentXxhash64(lines))));
}
} // namespace config
diff --git a/config/src/vespa/config/raw/rawsource.cpp b/config/src/vespa/config/raw/rawsource.cpp
index 2b38cff214c..a6c4da0b4dd 100644
--- a/config/src/vespa/config/raw/rawsource.cpp
+++ b/config/src/vespa/config/raw/rawsource.cpp
@@ -16,7 +16,7 @@ void
RawSource::getConfig()
{
auto lines(readConfig());
- ConfigValue value(lines, calculateContentMd5(lines));
+ ConfigValue value(lines, calculateContentXxhash64(lines));
_holder->handle(ConfigUpdate::UP(new ConfigUpdate(value, true, 1)));
}
diff --git a/config/src/vespa/config/retriever/configsnapshot.cpp b/config/src/vespa/config/retriever/configsnapshot.cpp
index 7aaf4ffd6f8..35f4415a26e 100644
--- a/config/src/vespa/config/retriever/configsnapshot.cpp
+++ b/config/src/vespa/config/retriever/configsnapshot.cpp
@@ -150,7 +150,7 @@ void
ConfigSnapshot::serializeValueV2(Cursor & cursor, const Value & value) const
{
cursor.setDouble("lastChanged", value.first);
- cursor.setString("md5", Memory(value.second.getMd5()));
+ cursor.setString("xxhash64", Memory(value.second.getXxhash64()));
value.second.serializeV2(cursor.setObject("payload"));
}
@@ -225,7 +225,7 @@ ConfigSnapshot::deserializeValueV1(Inspector & inspector) const
for (size_t i = 0; i < s.children(); i++) {
payload.push_back(s[i].asString().make_string());
}
- return Value(lastChanged, ConfigValue(payload, calculateContentMd5(payload)));
+ return Value(lastChanged, ConfigValue(payload, calculateContentXxhash64(payload)));
}
namespace {
@@ -247,10 +247,10 @@ std::pair<int64_t, ConfigValue>
ConfigSnapshot::deserializeValueV2(Inspector & inspector) const
{
int64_t lastChanged = static_cast<int64_t>(inspector["lastChanged"].asDouble());
- vespalib::string md5(inspector["md5"].asString().make_string());
+ vespalib::string xxhash64(inspector["xxhash64"].asString().make_string());
auto payload = std::make_unique<FixedPayload>();
copySlimeObject(inspector["payload"], payload->getData().setObject());
- return Value(lastChanged, ConfigValue(std::move(payload) , md5));
+ return Value(lastChanged, ConfigValue(std::move(payload), xxhash64));
}
}
diff --git a/config/src/vespa/config/set/configinstancesourcefactory.cpp b/config/src/vespa/config/set/configinstancesourcefactory.cpp
index 7f963847997..5ebffd4c4e9 100644
--- a/config/src/vespa/config/set/configinstancesourcefactory.cpp
+++ b/config/src/vespa/config/set/configinstancesourcefactory.cpp
@@ -14,8 +14,8 @@ public:
void close() override { }
void getConfig() override {
std::vector<vespalib::string> lines(_buffer.getlines());
- std::string currentMd5(config::calculateContentMd5(lines));
- _holder->handle(config::ConfigUpdate::UP(new config::ConfigUpdate(config::ConfigValue(lines, currentMd5), true, _generation)));
+ std::string currentXxhash64(config::calculateContentXxhash64(lines));
+ _holder->handle(config::ConfigUpdate::UP(new config::ConfigUpdate(config::ConfigValue(lines, currentXxhash64), true, _generation)));
}
void reload(int64_t generation) override { _generation = generation; }
diff --git a/config/src/vespa/config/set/configsetsource.cpp b/config/src/vespa/config/set/configsetsource.cpp
index e6ab890a9df..36c7d70e0d3 100644
--- a/config/src/vespa/config/set/configsetsource.cpp
+++ b/config/src/vespa/config/set/configsetsource.cpp
@@ -30,16 +30,16 @@ ConfigSetSource::getConfig()
AsciiConfigWriter writer(ss);
writer.write(*instance);
std::vector<vespalib::string> lines(ss.getlines());
- std::string currentMd5(calculateContentMd5(lines));
+ std::string currentXxhash64(calculateContentXxhash64(lines));
- if (isGenerationNewer(_generation, _lastState.generation) && currentMd5.compare(_lastState.md5) != 0) {
+ if (isGenerationNewer(_generation, _lastState.generation) && currentXxhash64.compare(_lastState.xxhash64) != 0) {
LOG(debug, "New generation, updating");
- _holder->handle(ConfigUpdate::UP(new ConfigUpdate(ConfigValue(lines, currentMd5), true, _generation)));
- _lastState.md5 = currentMd5;
+ _holder->handle(ConfigUpdate::UP(new ConfigUpdate(ConfigValue(lines, currentXxhash64), true, _generation)));
+ _lastState.xxhash64 = currentXxhash64;
_lastState.generation = _generation;
} else {
LOG(debug, "Sending timestamp update");
- _holder->handle(ConfigUpdate::UP(new ConfigUpdate(ConfigValue(lines, currentMd5), false, _generation)));
+ _holder->handle(ConfigUpdate::UP(new ConfigUpdate(ConfigValue(lines, currentXxhash64), false, _generation)));
_lastState.generation = _generation;
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/GetConfigContext.java b/configserver/src/main/java/com/yahoo/vespa/config/server/GetConfigContext.java
index ef7d8756228..7a3fa9fe46c 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/GetConfigContext.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/GetConfigContext.java
@@ -49,4 +49,10 @@ public class GetConfigContext {
public String logPre() {
return TenantRepository.logPre(app);
}
+
+ @Override
+ public String toString() {
+ return "get config context for application " + app + ", having handler " + requestHandler;
+ }
+
}
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 dd514e0d843..93148c52c3a 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
@@ -205,9 +205,8 @@ public class TenantApplications implements RequestHandler, HostValidator<Applica
@Override
public ConfigResponse resolveConfig(ApplicationId appId, GetConfigRequest req, Optional<Version> vespaVersion) {
Application application = getApplication(appId, vespaVersion);
- if (log.isLoggable(Level.FINE)) {
- log.log(Level.FINE, TenantRepository.logPre(appId) + "Resolving for tenant '" + tenant + "' with handler for application '" + application + "'");
- }
+ log.log(Level.FINE, () -> TenantRepository.logPre(appId) + "Resolving for tenant '" + tenant +
+ "' with handler for application '" + application + "'");
return application.resolveConfig(req, responseFactory);
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index 0198e5899da..8dedb5a29c9 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -187,7 +187,6 @@ public class ModelContextImpl implements ModelContext {
private final double resourceLimitMemory;
private final double minNodeRatioPerGroup;
private final int metricsproxyNumThreads;
- private final boolean enforceRankProfileInheritance;
private final boolean newLocationBrokerLogic;
private final boolean containerDumpHeapOnShutdownTimeout;
private final double containerShutdownTimeout;
@@ -216,7 +215,6 @@ public class ModelContextImpl implements ModelContext {
this.resourceLimitMemory = flagValue(source, appId, PermanentFlags.RESOURCE_LIMIT_MEMORY);
this.minNodeRatioPerGroup = flagValue(source, appId, Flags.MIN_NODE_RATIO_PER_GROUP);
this.metricsproxyNumThreads = flagValue(source, appId, Flags.METRICSPROXY_NUM_THREADS);
- this.enforceRankProfileInheritance = flagValue(source, appId, Flags.ENFORCE_RANK_PROFILE_INHERITANCE);
this.newLocationBrokerLogic = flagValue(source, appId, Flags.NEW_LOCATION_BROKER_LOGIC);
this.containerDumpHeapOnShutdownTimeout = flagValue(source, appId, Flags.CONTAINER_DUMP_HEAP_ON_SHUTDOWN_TIMEOUT);
this.containerShutdownTimeout = flagValue(source, appId,Flags.CONTAINER_SHUTDOWN_TIMEOUT);
@@ -252,8 +250,6 @@ public class ModelContextImpl implements ModelContext {
@Override public double containerShutdownTimeout() { return containerShutdownTimeout; }
@Override public boolean containerDumpHeapOnShutdownTimeout() { return containerDumpHeapOnShutdownTimeout; }
- @Override public boolean enforceRankProfileInheritance() { return enforceRankProfileInheritance; }
-
private static <V> V flagValue(FlagSource source, ApplicationId appId, UnboundFlag<? extends V, ?, ?> flag) {
return flag.bindTo(source)
.with(FetchVector.Dimension.APPLICATION_ID, appId.serializedForm())
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java
index bad03862133..412eafd9882 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/GetConfigProcessor.java
@@ -27,8 +27,8 @@ import java.util.logging.Logger;
import static com.yahoo.vespa.config.protocol.SlimeConfigResponse.fromConfigPayload;
/**
-* @author hmusum
-*/
+ * @author hmusum
+ */
class GetConfigProcessor implements Runnable {
private static final Logger log = Logger.getLogger(GetConfigProcessor.class.getName());
@@ -69,7 +69,7 @@ class GetConfigProcessor implements Runnable {
// TODO: Increment statistics (Metrics) failed counters when requests fail
public Pair<GetConfigContext, Long> getConfig(JRTServerConfigRequest request) {
- //Request has already been detached
+ // Request has already been detached
if ( ! request.validateParameters()) {
// Error code is set in verifyParameters if parameters are not OK.
log.log(Level.WARNING, "Parameters for request " + request + " did not validate: " + request.errorCode() + " : " + request.errorMessage());
@@ -140,6 +140,7 @@ class GetConfigProcessor implements Runnable {
}
return null;
}
+
@Override
public void run() {
rpcServer.hostLivenessTracker().receivedRequestFrom(request.getClientHostName());
diff --git a/configserver/src/main/sh/start-configserver b/configserver/src/main/sh/start-configserver
index f9eb5e3a0a5..965dc8756be 100755
--- a/configserver/src/main/sh/start-configserver
+++ b/configserver/src/main/sh/start-configserver
@@ -188,7 +188,6 @@ vespa-run-as-vespa-user vespa-runserver -s configserver -r 30 -p $pidfile -- \
-Djdisc.config.file=$cfpfile \
-Djdisc.export.packages= \
-Djdisc.cache.path=$bundlecachedir \
- -Djdisc.debug.resources=false \
-Djdisc.bundle.path=${VESPA_HOME}/lib/jars \
-Djdisc.logger.enabled=true \
-Djdisc.logger.level=ALL \
diff --git a/container-core/src/main/sh/find-pid b/container-core/src/main/sh/find-pid
index e1c2195c805..56c387387d4 100755
--- a/container-core/src/main/sh/find-pid
+++ b/container-core/src/main/sh/find-pid
@@ -77,20 +77,26 @@ findhost
set -euo pipefail
if (( $# != 1 )); then
- echo "Usage: $0 <service-config-id>" >&2
+ echo "Usage: $0 <service-name-or-config-id>" >&2
exit 1
fi
-readonly config_id=$1
-status=$(vespa-sentinel-cmd list 2>/dev/null | grep "id=\"${config_id}\"")
-if [ -z "${status}" ]; then
- echo "No service named '${config_id}'" >&2
- exit 1
-fi
-readonly pid=$(echo ${status} | cut -d " " -f 4 | cut -d "=" -f 2)
-if ! [[ "${pid}" =~ ^[0-9]+$ ]]; then
- echo "Could not find valid pid for '${config_id}' (pid='${pid}')" >&2
- exit 1
+readonly service=$1
+readonly pid_file="$VESPA_HOME/var/run/$service.pid"
+if [ -f "$pid_file" ]; then
+ parent_pid=$(cat "$pid_file")
+ pid=$(pgrep --parent $parent_pid)
+else
+ status=$(vespa-sentinel-cmd list 2>/dev/null | grep --perl-regexp "($service state=|id=\"$service\")")
+ if [ -z "${status}" ]; then
+ echo "No service with name or config id '${service}'" >&2
+ exit 1
+ fi
+ pid=$(echo ${status} | cut -d " " -f 4 | cut -d "=" -f 2)
+ if ! [[ "${pid}" =~ ^[0-9]+$ ]]; then
+ echo "Could not find valid pid for '${service}' (pid='${pid}')" >&2
+ exit 1
+ fi
fi
if [ -z "$(ps -p ${pid} -o pid=)" ]; then
echo "Could not find process for '${pid}'" >&2
diff --git a/container-disc/pom.xml b/container-disc/pom.xml
index 7d6a1ebb9d5..a362fb11be6 100644
--- a/container-disc/pom.xml
+++ b/container-disc/pom.xml
@@ -177,12 +177,14 @@
linguistics-components-jar-with-dependencies.jar,
vespaclient-container-plugin-jar-with-dependencies.jar,
vespa-athenz-jar-with-dependencies.jar,
+ container-apache-http-client-bundle-jar-with-dependencies.jar, <!-- Apache http client repackaged as bundle -->
+
+ <!-- Vespa security utils with necessary 3rd party bundles -->
security-utils-jar-with-dependencies.jar,
- zkfacade-jar-with-dependencies.jar,
- zookeeper-server-jar-with-dependencies.jar,
- <!-- Apache http client repackaged as bundle -->
- container-apache-http-client-bundle-jar-with-dependencies.jar,
- <!-- Jetty -->
+ bcpkix-jdk15on-${bouncycastle.version}.jar,
+ bcprov-jdk15on-${bouncycastle.version}.jar,
+
+ <!-- Jetty -->
alpn-api-${jetty-alpn.version}.jar,
http2-server-${jetty.version}.jar,
http2-common-${jetty.version}.jar,
@@ -199,6 +201,8 @@
jetty-servlets-${jetty.version}.jar,
jetty-util-${jetty.version}.jar,
jetty-util-ajax-${jetty.version}.jar,
+ javax.servlet-api-3.1.0.jar,
+
<!-- Spifly (required for OSGi service loader used by Jetty) -->
org.apache.aries.spifly.dynamic.bundle-${spifly.version}.jar,
asm-${asm.version}.jar,
@@ -207,10 +211,7 @@
asm-tree-${asm.version}.jar,
asm-util-${asm.version}.jar,
<!-- Spifly end -->
- <!-- Misc 3rd party bundles -->
- bcpkix-jdk15on-${bouncycastle.version}.jar,
- bcprov-jdk15on-${bouncycastle.version}.jar,
- javax.servlet-api-3.1.0.jar,
+
<!-- Jersey 2 + Jackson 2 -->
aopalliance-repackaged-${hk2.version}.jar,
hk2-api-${hk2.version}.jar,
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
index 457d4fc96a9..a84d2521b8b 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/ConfiguredApplication.java
@@ -6,6 +6,7 @@ import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.yahoo.cloud.config.SlobroksConfig;
+import com.yahoo.component.Vtag;
import com.yahoo.component.provider.ComponentRegistry;
import com.yahoo.concurrent.DaemonThreadFactory;
import com.yahoo.config.ConfigInstance;
@@ -102,7 +103,7 @@ public final class ConfiguredApplication implements Application {
static {
LogSetup.initVespaLogging("Container");
- log.log(Level.INFO, "Starting container");
+ log.log(Level.INFO, "Starting jdisc" + (Vtag.currentVersion.isEmpty() ? "" : " at version " + Vtag.currentVersion));
}
/**
diff --git a/container-disc/src/main/sh/vespa-start-container-daemon.sh b/container-disc/src/main/sh/vespa-start-container-daemon.sh
index f09a17de5e5..2d9bbcf21fc 100755
--- a/container-disc/src/main/sh/vespa-start-container-daemon.sh
+++ b/container-disc/src/main/sh/vespa-start-container-daemon.sh
@@ -212,7 +212,6 @@ exec $numactlcmd $envcmd java \
-Djdisc.config.file="$cfpfile" \
-Djdisc.export.packages=${jdisc_export_packages} \
-Djdisc.cache.path="$bundlecachedir" \
- -Djdisc.debug.resources=false \
-Djdisc.bundle.path="${VESPA_HOME}/lib/jars" \
-Djdisc.logger.enabled=false \
-Djdisc.logger.level=ALL \
diff --git a/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/NetworkMultiplexerHolder.java b/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/NetworkMultiplexerHolder.java
index 89ecc931efb..48657de1910 100644
--- a/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/NetworkMultiplexerHolder.java
+++ b/container-messagebus/src/main/java/com/yahoo/container/jdisc/messagebus/NetworkMultiplexerHolder.java
@@ -39,7 +39,7 @@ public class NetworkMultiplexerHolder extends AbstractComponent {
public void deconstruct() {
synchronized (monitor) {
if (net != null) {
- net.destroy();
+ net.disown();
net = null;
}
destroyed = true;
diff --git a/container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java b/container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java
index 922e4140868..009d1619503 100644
--- a/container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java
+++ b/container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusClient.java
@@ -9,6 +9,8 @@ import com.yahoo.jdisc.handler.ContentChannel;
import com.yahoo.jdisc.handler.RequestDeniedException;
import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.service.ClientProvider;
+
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import com.yahoo.messagebus.EmptyReply;
import com.yahoo.messagebus.Error;
@@ -29,9 +31,10 @@ import java.util.logging.Logger;
public final class MbusClient extends AbstractResource implements ClientProvider, ReplyHandler {
private static final Logger log = Logger.getLogger(MbusClient.class.getName());
+ private static final AtomicInteger threadId = new AtomicInteger(0);
private final BlockingQueue<MbusRequest> queue = new LinkedBlockingQueue<>();
private final ClientSession session;
- private final Thread thread = new Thread(new SenderTask(), "MbusClient");
+ private final Thread thread;
private volatile boolean done = false;
private final ResourceReference sessionReference;
@@ -39,6 +42,7 @@ public final class MbusClient extends AbstractResource implements ClientProvider
public MbusClient(ClientSession session) {
this.session = session;
this.sessionReference = session.refer();
+ thread = new Thread(new SenderTask(), "mbus-client-" + threadId.getAndIncrement());
}
@Override
diff --git a/container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java b/container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java
index 67badddddd2..a165a0b8c64 100644
--- a/container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java
+++ b/container-messagebus/src/main/java/com/yahoo/messagebus/jdisc/MbusServer.java
@@ -10,13 +10,19 @@ import com.yahoo.jdisc.handler.ContentChannel;
import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.service.CurrentContainer;
import com.yahoo.jdisc.service.ServerProvider;
+
+import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
-import com.yahoo.messagebus.*;
+
+import com.yahoo.messagebus.EmptyReply;
import com.yahoo.messagebus.Error;
+import com.yahoo.messagebus.ErrorCode;
+import com.yahoo.messagebus.Message;
+import com.yahoo.messagebus.MessageHandler;
+import com.yahoo.messagebus.Reply;
import com.yahoo.messagebus.shared.ServerSession;
import java.net.URI;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
/**
@@ -24,8 +30,9 @@ import java.util.logging.Logger;
*/
public final class MbusServer extends AbstractResource implements ServerProvider, MessageHandler {
+ private enum State {INITIALIZING, RUNNING, STOPPED}
private final static Logger log = Logger.getLogger(MbusServer.class.getName());
- private final AtomicBoolean running = new AtomicBoolean(false);
+ private final AtomicReference<State> runState = new AtomicReference<>(State.INITIALIZING);
private final CurrentContainer container;
private final ServerSession session;
private final URI uri;
@@ -44,26 +51,33 @@ public final class MbusServer extends AbstractResource implements ServerProvider
public void start() {
log.log(Level.FINE, "Starting message bus server.");
session.connect();
- running.set(true);
+ runState.set(State.RUNNING);
}
@Override
public void close() {
log.log(Level.FINE, "Closing message bus server.");
- running.set(false);
+ runState.set(State.STOPPED);
}
@Override
protected void destroy() {
log.log(Level.FINE, "Destroying message bus server.");
- running.set(false);
+ runState.set(State.STOPPED);
sessionReference.close();
}
@Override
public void handleMessage(Message msg) {
- if (!running.get()) {
- dispatchErrorReply(msg, ErrorCode.SESSION_BUSY, "Session temporarily closed.");
+ State state = runState.get();
+ if (state == State.INITIALIZING) {
+ dispatchErrorReply(msg, ErrorCode.SESSION_BUSY, "MBusServer not started.");
+ return;
+ }
+ if (state == State.STOPPED) {
+ // We might need to detect requests originating from the same JVM, as they nede to fail fast
+ // As they are holding references to the container preventing proper shutdown.
+ dispatchErrorReply(msg, ErrorCode.SESSION_BUSY, "MBusServer has been closed.");
return;
}
if (msg.getTrace().shouldTrace(6)) {
diff --git a/container-messagebus/src/main/java/com/yahoo/messagebus/shared/ClientSession.java b/container-messagebus/src/main/java/com/yahoo/messagebus/shared/ClientSession.java
index 0964a254cf2..22322a49a78 100644
--- a/container-messagebus/src/main/java/com/yahoo/messagebus/shared/ClientSession.java
+++ b/container-messagebus/src/main/java/com/yahoo/messagebus/shared/ClientSession.java
@@ -10,5 +10,5 @@ import com.yahoo.messagebus.Result;
*/
public interface ClientSession extends SharedResource {
- public Result sendMessage(Message msg);
+ Result sendMessage(Message msg);
}
diff --git a/container-messagebus/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java b/container-messagebus/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java
index bf89f3869ed..0c387456cf3 100644
--- a/container-messagebus/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java
+++ b/container-messagebus/src/test/java/com/yahoo/messagebus/jdisc/MbusServerConformanceTest.java
@@ -31,6 +31,7 @@ import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import static com.yahoo.messagebus.ErrorCode.APP_FATAL_ERROR;
+import static com.yahoo.messagebus.ErrorCode.SEND_QUEUE_CLOSED;
import static com.yahoo.messagebus.ErrorCode.SESSION_BUSY;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/NoopRoleService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/NoopRoleService.java
index 970a70c6885..341b521212e 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/NoopRoleService.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/NoopRoleService.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.integration.aws;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import java.util.List;
import java.util.Optional;
@@ -12,7 +13,7 @@ import java.util.Optional;
public class NoopRoleService implements RoleService {
@Override
- public Optional<TenantRoles> createTenantRole(TenantName tenant) {
+ public Optional<TenantRoles> createTenantRole(Tenant tenant) {
return Optional.empty();
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/RoleService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/RoleService.java
index d27fa0a5bd8..61007b9ff46 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/RoleService.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/aws/RoleService.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.integration.aws;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import java.util.List;
import java.util.Optional;
@@ -11,7 +12,7 @@ import java.util.Optional;
*/
public interface RoleService {
- Optional<TenantRoles> createTenantRole(TenantName tenant);
+ Optional<TenantRoles> createTenantRole(Tenant tenant);
/** Retrieve the names of the tenant roles (host and container). Does not guarantee these roles exist */
TenantRoles getTenantRole(TenantName tenant);
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java
index 7539ef3c63a..f650f71c0ec 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java
@@ -8,6 +8,7 @@ import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.UpgradePolicy;
import com.yahoo.config.provision.zone.ZoneApi;
@@ -89,4 +90,7 @@ public interface ZoneRegistry {
/** Returns a URL to the controller's api endpoint */
URI apiUrl();
+ /** IAM tenant developer role ARN */
+ Optional<String> tenantDeveloperRoleArn(TenantName tenant);
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java
index 7fa46031c98..7fa46031c98 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/AthenzTenant.java
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java
index 1060b118beb..1060b118beb 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/CloudTenant.java
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java
index cf6d73cb8f8..cf6d73cb8f8 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/DeletedTenant.java
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/LastLoginInfo.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/LastLoginInfo.java
index 15f2f97e7d1..15f2f97e7d1 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/LastLoginInfo.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/LastLoginInfo.java
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java
index 80982d70107..80982d70107 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/Tenant.java
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java
index a20477d7aab..81c08e1083b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfo.java
@@ -1,10 +1,6 @@
// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.tenant;
-import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
-
-import java.util.ArrayList;
-import java.util.List;
import java.util.Objects;
/**
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java
index a12f351abd6..a12f351abd6 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoAddress.java
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java
index a00dd626f0a..a00dd626f0a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/TenantInfoBillingContact.java
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/package-info.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/package-info.java
index 9218bfcd850..9218bfcd850 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tenant/package-info.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/tenant/package-info.java
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDb.java
index ca173437dd1..a7555307a59 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDb.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/archive/CuratorArchiveBucketDb.java
@@ -33,8 +33,6 @@ public class CuratorArchiveBucketDb {
* Policy size limit is 20kb, about 550 bytes for non-tenant related policies. Each tenant
* needs about 500 + len(role_arn) bytes, we limit role_arn to 100 characters, so we can
* fit about (20k - 550) / 600 ~ 32 tenants per bucket.
- *
- * This limit is only enforced for public systems as non-public systems does not use tenant specific policies.
*/
private final static int TENANTS_PER_BUCKET = 30;
@@ -86,7 +84,7 @@ public class CuratorArchiveBucketDb {
.orElseGet(() -> {
// If not, find an existing bucket with space
Optional<ArchiveBucket> unfilledBucket = zoneBuckets.stream()
- .filter(bucket -> !system.isPublic() || bucket.tenants().size() < TENANTS_PER_BUCKET)
+ .filter(bucket -> bucket.tenants().size() < TENANTS_PER_BUCKET)
.findAny();
// And place the tenant in that bucket.
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainer.java
index b096a853541..6c4f5ffff9d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ArchiveAccessMaintainer.java
@@ -2,16 +2,25 @@
package com.yahoo.vespa.hosted.controller.maintenance;
import com.google.common.collect.Maps;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.config.provision.zone.ZoneApi;
+import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.jdisc.Metric;
+import com.yahoo.vespa.flags.BooleanFlag;
+import com.yahoo.vespa.flags.FetchVector;
+import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveService;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import com.yahoo.vespa.hosted.controller.archive.CuratorArchiveBucketDb;
+import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import java.time.Duration;
+import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -27,6 +36,8 @@ public class ArchiveAccessMaintainer extends ControllerMaintainer {
private final ArchiveService archiveService;
private final ZoneRegistry zoneRegistry;
private final Metric metric;
+ private final BooleanFlag archiveEnabled;
+ private final BooleanFlag developerRoleEnabled;
public ArchiveAccessMaintainer(Controller controller, Metric metric, Duration interval) {
super(controller, interval);
@@ -34,6 +45,8 @@ public class ArchiveAccessMaintainer extends ControllerMaintainer {
this.archiveService = controller.serviceRegistry().archiveService();
this.zoneRegistry = controller().zoneRegistry();
this.metric = metric;
+ this.archiveEnabled = Flags.ENABLE_ONPREM_TENANT_S3_ARCHIVE.bindTo(controller().flagSource());
+ this.developerRoleEnabled = Flags.ENABLE_TENANT_DEVELOPER_ROLE.bindTo(controller().flagSource());
}
@Override
@@ -43,22 +56,45 @@ public class ArchiveAccessMaintainer extends ControllerMaintainer {
metric.set(bucketCountMetricName, archiveBucketDb.buckets(zoneId).size(),
metric.createContext(Map.of("zone", zoneId.value()))));
- var tenantArchiveAccessRoles = controller().tenants().asList().stream()
- .filter(t -> t instanceof CloudTenant)
- .map(t -> (CloudTenant) t)
- .filter(t -> t.archiveAccessRole().isPresent())
- .collect(Collectors.toUnmodifiableMap(
- Tenant::name, cloudTenant -> cloudTenant.archiveAccessRole().orElseThrow()));
- zoneRegistry.zones().controllerUpgraded().ids().forEach(zoneId ->
- archiveBucketDb.buckets(zoneId).forEach(archiveBucket ->
- archiveService.updateBucketAndKeyPolicy(zoneId, archiveBucket,
- Maps.filterEntries(tenantArchiveAccessRoles,
- entry -> archiveBucket.tenants().contains(entry.getKey())))
- )
+ zoneRegistry.zones().controllerUpgraded().zones().forEach(z -> {
+ ZoneId zoneId = z.getId();
+ var tenantArchiveAccessRoles = tenantArchiveAccessRoles(z);
+ archiveBucketDb.buckets(zoneId).forEach(archiveBucket ->
+ archiveService.updateBucketAndKeyPolicy(zoneId, archiveBucket,
+ Maps.filterEntries(tenantArchiveAccessRoles,
+ entry -> archiveBucket.tenants().contains(entry.getKey())))
+ );
+ }
);
return 1.0;
}
+ private Map<TenantName, String> tenantArchiveAccessRoles(ZoneApi zone) {
+ List<Tenant> tenants = controller().tenants().asList();
+ if (zoneRegistry.system().isPublic()) {
+ return tenants.stream()
+ .filter(t -> t instanceof CloudTenant)
+ .map(t -> (CloudTenant) t)
+ .filter(t -> t.archiveAccessRole().isPresent())
+ .collect(Collectors.toUnmodifiableMap(
+ Tenant::name, cloudTenant -> cloudTenant.archiveAccessRole().orElseThrow()));
+ } else {
+ return tenants.stream()
+ .filter(t -> t instanceof AthenzTenant
+ && enabled(archiveEnabled, t, zone) && enabled(developerRoleEnabled, t, zone))
+ .map(Tenant::name)
+ .collect(Collectors.toUnmodifiableMap(
+ Function.identity(), t -> zoneRegistry.tenantDeveloperRoleArn(t).orElseThrow()));
+
+ }
+ }
+
+ private boolean enabled(BooleanFlag flag, Tenant tenant, ZoneApi zone) {
+ return flag.with(FetchVector.Dimension.TENANT_ID, tenant.name().value())
+ .with(FetchVector.Dimension.ZONE_ID, zone.getId().value())
+ .value();
+ }
+
}
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 ce6f9c802d6..d2b43dc63d9 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
@@ -27,9 +27,7 @@ public class TenantRoleMaintainer extends ControllerMaintainer {
var tenants = controller().tenants().asList();
// Create separate athenz service for all tenants
- tenants.stream()
- .map(Tenant::name)
- .forEach(roleService::createTenantRole);
+ tenants.forEach(roleService::createTenantRole);
// Until we have moved to separate athenz service per tenant, make sure we update the shared policy
// to allow ssh logins for hosts in prod/perf with a separate tenant iam role.
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 1a2acb82348..98ac789de04 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -1967,6 +1967,8 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
.flatMap(options -> optional("vespaVersion", options))
.map(Version::fromString);
+ ensureApplicationExists(TenantAndApplicationId.from(id), request);
+
controller.jobController().deploy(id, type, version, applicationPackage);
RunId runId = controller.jobController().last(id, type).get().id();
Slime slime = new Slime();
@@ -2617,6 +2619,8 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
applicationPackage,
Optional.of(requireUserPrincipal(request)));
+ ensureApplicationExists(TenantAndApplicationId.from(tenant, application), request);
+
return JobControllerApiHandlerHelper.submitResponse(controller.jobController(),
tenant,
application,
@@ -2718,5 +2722,13 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
.anyMatch(definition -> definition == RoleDefinition.hostedOperator);
}
+ private void ensureApplicationExists(TenantAndApplicationId id, HttpRequest request) {
+ if (controller.applications().getApplication(id).isEmpty()) {
+ log.fine("Application does not exist in public, creating: " + id);
+ var credentials = accessControlRequests.credentials(id.tenant(), null /* not used on public */ , request.getJDiscRequest());
+ controller.applications().createApplication(id, credentials);
+ }
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
index 157f57b3bea..7e88f127026 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
@@ -20,10 +20,7 @@ import com.yahoo.slime.SlimeStream;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.text.Text;
import com.yahoo.vespa.configserver.flags.FlagsDb;
-import com.yahoo.vespa.flags.BooleanFlag;
-import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.FlagSource;
-import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.IntFlag;
import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.controller.Controller;
@@ -71,9 +68,7 @@ public class UserApiHandler extends LoggingRequestHandler {
private final UserManagement users;
private final Controller controller;
private final FlagsDb flagsDb;
- private final BooleanFlag enable_public_signup_flow;
private final IntFlag maxTrialTenants;
- private final BooleanFlag enabledHorizonDashboard;
@Inject
public UserApiHandler(Context parentCtx, UserManagement users, Controller controller, FlagSource flagSource, FlagsDb flagsDb) {
@@ -81,9 +76,7 @@ public class UserApiHandler extends LoggingRequestHandler {
this.users = users;
this.controller = controller;
this.flagsDb = flagsDb;
- this.enable_public_signup_flow = PermanentFlags.ENABLE_PUBLIC_SIGNUP_FLOW.bindTo(flagSource);
this.maxTrialTenants = PermanentFlags.MAX_TRIAL_TENANTS.bindTo(flagSource);
- this.enabledHorizonDashboard = Flags.ENABLED_HORIZON_DASHBOARD.bindTo(flagSource);
}
@Override
@@ -173,9 +166,6 @@ public class UserApiHandler extends LoggingRequestHandler {
root.setBool("isPublic", controller.system().isPublic());
root.setBool("isCd", controller.system().isCd());
- // TODO (freva): Remove after users have migrated to use 'flags'
- root.setBool(enable_public_signup_flow.id().toString(),
- enable_public_signup_flow.with(FetchVector.Dimension.CONSOLE_USER_EMAIL, user.email()).value());
root.setBool("hasTrialCapacity", hasTrialCapacity());
toSlime(root.setObject("user"), user);
@@ -190,10 +180,6 @@ public class UserApiHandler extends LoggingRequestHandler {
Cursor tenantRolesObject = tenantObject.setArray("roles");
tenantRolesByTenantName.getOrDefault(tenant, List.of())
.forEach(role -> tenantRolesObject.addString(role.definition().name()));
- if (controller.system().isPublic()) {
- tenantObject.setBool(enabledHorizonDashboard.id().toString(),
- enabledHorizonDashboard.with(FetchVector.Dimension.TENANT_ID, tenant.value()).value());
- }
});
if (!operatorRoles.isEmpty()) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
index fe03b69a3fe..7ce17aff782 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
@@ -9,6 +9,7 @@ import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.UpgradePolicy;
import com.yahoo.config.provision.zone.ZoneApi;
@@ -216,6 +217,8 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry
return URI.create("https://api.tld:4443/");
}
+ @Override public Optional<String> tenantDeveloperRoleArn(TenantName tenant) { return Optional.empty(); }
+
@Override
public boolean hasZone(ZoneId zoneId) {
return zones.stream().anyMatch(zone -> zone.getId().equals(zoneId));
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
index 5f76a30bf45..8c6d368d93a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
@@ -1,6 +1,7 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.restapi.application;
+import ai.vespa.hosted.api.MultiPartStreamer;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.InstanceName;
@@ -10,10 +11,12 @@ import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.LockedTenant;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
+import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
@@ -232,6 +235,42 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
200);
}
+ @Test
+ public void create_application_on_deploy() {
+ var application = ApplicationName.from("unique");
+ var applicationPackage = new ApplicationPackageBuilder().build();
+
+ assertTrue(tester.controller().applications().getApplication(TenantAndApplicationId.from(tenantName, application)).isEmpty());
+
+ tester.assertResponse(
+ request("/application/v4/tenant/scoober/application/unique/instance/default/deploy/dev-aws-us-east-1c", POST)
+ .data(createApplicationDeployData(Optional.of(applicationPackage), Optional.empty(), true))
+ .roles(Set.of(Role.developer(tenantName))),
+ "{\"message\":\"Deployment started in run 1 of dev-aws-us-east-1c for scoober.unique. This may take about 15 minutes the first time.\",\"run\":1}");
+
+ assertTrue(tester.controller().applications().getApplication(TenantAndApplicationId.from(tenantName, application)).isPresent());
+ }
+
+ @Test
+ public void create_application_on_submit() {
+ var application = ApplicationName.from("unique");
+ var applicationPackage = new ApplicationPackageBuilder()
+ .trustDefaultCertificate()
+ .build();
+
+ assertTrue(tester.controller().applications().getApplication(TenantAndApplicationId.from(tenantName, application)).isEmpty());
+
+ var data = ApplicationApiTest.createApplicationSubmissionData(applicationPackage, 123);
+
+ tester.assertResponse(
+ request("/application/v4/tenant/scoober/application/unique/submit", POST)
+ .data(data)
+ .roles(Set.of(Role.developer(tenantName))),
+ "{\"message\":\"Application package version: 1.0.1-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}");
+
+ assertTrue(tester.controller().applications().getApplication(TenantAndApplicationId.from(tenantName, application)).isPresent());
+ }
+
private ApplicationPackageBuilder prodBuilder() {
return new ApplicationPackageBuilder()
.instances("default")
@@ -264,8 +303,31 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
JobType.productionAwsUsEast1c,
Optional.empty(),
applicationPackage);
+ }
+ private MultiPartStreamer createApplicationDeployData(Optional<ApplicationPackage> applicationPackage,
+ Optional<ApplicationVersion> applicationVersion, boolean deployDirectly) {
+ MultiPartStreamer streamer = new MultiPartStreamer();
+ streamer.addJson("deployOptions", deployOptions(deployDirectly, applicationVersion));
+ applicationPackage.ifPresent(ap -> streamer.addBytes("applicationZip", ap.zippedContent()));
+ return streamer;
+ }
+
+ private String deployOptions(boolean deployDirectly, Optional<ApplicationVersion> applicationVersion) {
+ return "{\"vespaVersion\":null," +
+ "\"ignoreValidationErrors\":false," +
+ "\"deployDirectly\":" + deployDirectly +
+ applicationVersion.map(version ->
+ "," +
+ "\"buildNumber\":" + version.buildNumber().getAsLong() + "," +
+ "\"sourceRevision\":{" +
+ "\"repository\":\"" + version.source().get().repository() + "\"," +
+ "\"branch\":\"" + version.source().get().branch() + "\"," +
+ "\"commit\":\"" + version.source().get().commit() + "\"" +
+ "}"
+ ).orElse("") +
+ "}";
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index fb860063696..596a0b186db 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -1,4 +1,4 @@
-// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.restapi.application;
import ai.vespa.hosted.api.MultiPartStreamer;
@@ -250,7 +250,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
var app1 = deploymentTester.newDeploymentContext(id);
// POST (deploy) an application to start a manual deployment in prod is not allowed
- MultiPartStreamer entity = createApplicationDeployData(applicationPackageInstance1, true);
+ MultiPartStreamer entity = createApplicationDeployData(applicationPackageInstance1);
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/production-us-east-3/", POST)
.data(entity)
.userIdentity(USER_ID),
@@ -289,7 +289,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// POST an application package is not generally allowed under user instance
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/otheruser/deploy/dev-us-east-1", POST)
.userIdentity(OTHER_USER_ID)
- .data(createApplicationDeployData(applicationPackageInstance1, false)),
+ .data(createApplicationDeployData(applicationPackageInstance1)),
accessDenied,
403);
@@ -303,7 +303,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// POST an application package is not allowed under user instance for tenant admins
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/myuser/deploy/dev-us-east-1", POST)
.userIdentity(USER_ID)
- .data(createApplicationDeployData(applicationPackageInstance1, false)),
+ .data(createApplicationDeployData(applicationPackageInstance1)),
new File("deployment-job-accepted-2.json"));
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/myuser/job/dev-us-east-1/diff/1", GET).userIdentity(HOSTED_VESPA_OPERATOR),
@@ -1026,38 +1026,24 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
@Test
- public void testDeployDirectly() {
+ public void testDeployWithApplicationPackage() {
// Setup
- createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR));
- // Create tenant
- tester.assertResponse(request("/application/v4/tenant/tenant1", POST).userIdentity(USER_ID)
- .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
- new File("tenant-without-applications.json"));
-
- // Create application
- tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST)
- .userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
- new File("instance-reference.json"));
-
- // Add build service to operator role
- addUserToHostedOperatorRole(HostedAthenzIdentities.from(SCREWDRIVER_ID));
-
// POST (deploy) a system application with an application package
- MultiPartStreamer noAppEntity = createApplicationDeployData(Optional.empty(), true);
+ MultiPartStreamer noAppEntity = createApplicationDeployData(Optional.empty());
tester.assertResponse(request("/application/v4/tenant/hosted-vespa/application/routing/environment/prod/region/us-central-1/instance/default/deploy", POST)
.data(noAppEntity)
.userIdentity(HOSTED_VESPA_OPERATOR),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Deployment of system applications during a system upgrade is not allowed\"}",
400);
- deploymentTester.controllerTester().upgradeSystem(deploymentTester.controller().readVersionStatus().controllerVersion().get().versionNumber());
+ deploymentTester.controllerTester()
+ .upgradeSystem(deploymentTester.controller().readVersionStatus().controllerVersion().get()
+ .versionNumber());
tester.assertResponse(request("/application/v4/tenant/hosted-vespa/application/routing/environment/prod/region/us-central-1/instance/default/deploy", POST)
- .data(noAppEntity)
- .userIdentity(HOSTED_VESPA_OPERATOR),
- new File("deploy-result.json"));
+ .data(noAppEntity)
+ .userIdentity(HOSTED_VESPA_OPERATOR),
+ new File("deploy-result.json"));
}
@Test
@@ -1113,7 +1099,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
@Test
- public void testErrorResponses() throws Exception {
+ public void testErrorResponses() {
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
// PUT (update) non-existing tenant returns 403 as tenant access cannot be determined when the tenant does not exist
@@ -1223,7 +1209,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
400);
// POST (deploy) an application to legacy deploy path
- MultiPartStreamer entity = createApplicationDeployData(applicationPackageInstance1, true);
+ MultiPartStreamer entity = createApplicationDeployData(applicationPackageInstance1);
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/dev/region/us-east-1/instance/instance1/deploy", POST)
.data(entity)
.userIdentity(USER_ID),
@@ -1330,7 +1316,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
200);
// Deploy to an authorized zone by a user tenant is disallowed
- MultiPartStreamer entity = createApplicationDeployData(applicationPackageDefault, true);
+ MultiPartStreamer entity = createApplicationDeployData(applicationPackageDefault);
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/default/deploy", POST)
.data(entity)
.userIdentity(USER_ID),
@@ -1437,7 +1423,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
.build();
createTenantAndApplication();
- MultiPartStreamer entity = createApplicationDeployData(applicationPackage, true);
+ MultiPartStreamer entity = createApplicationDeployData(applicationPackage);
// POST (deploy) an application to dev through a deployment job, with user instance and a proper tenant
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/new-user/deploy/dev-us-east-1", POST)
.data(entity)
@@ -1484,7 +1470,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
.build();
// deploy the application to a dev zone. Should fail since the developer is not authorized to launch the service
- MultiPartStreamer entity = createApplicationDeployData(applicationPackage, true);
+ MultiPartStreamer entity = createApplicationDeployData(applicationPackage);
tester.assertResponse(request("/application/v4/tenant/sandbox/application/myapp/instance/default/deploy/dev-us-east-1", POST)
.data(entity)
.userIdentity(developer),
@@ -1646,7 +1632,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
@Test
- public void testServiceView() throws Exception {
+ public void testServiceView() {
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
String serviceApi="/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/service";
// Not allowed to request apis not listed in feature flag allowed-service-view-apis. e.g /document/v1
@@ -1671,6 +1657,36 @@ public class ApplicationApiTest extends ControllerContainerTest {
403);
}
+ @Test
+ public void create_application_on_deploy() {
+ // Setup
+ createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
+ addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR));
+
+ // Create tenant
+ tester.assertResponse(request("/application/v4/tenant/tenant1", POST).userIdentity(USER_ID)
+ .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
+ new File("tenant-without-applications.json"));
+
+ // Deploy application
+ var id = ApplicationId.from("tenant1", "application1", "instance1");
+ var appId = TenantAndApplicationId.from(id);
+ var entity = createApplicationDeployData(applicationPackageInstance1);
+
+ assertTrue(tester.controller().applications().getApplication(appId).isEmpty());
+
+ // POST (deploy) an application to start a manual deployment to dev
+ tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/dev-us-east-1/", POST)
+ .data(entity)
+ .oktaIdentityToken(OKTA_IT)
+ .oktaAccessToken(OKTA_AT)
+ .userIdentity(USER_ID),
+ "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}");
+
+ assertTrue(tester.controller().applications().getApplication(appId).isPresent());
+ }
+
private static String serializeInstant(Instant i) {
return DateTimeFormatter.ISO_INSTANT.format(i.truncatedTo(ChronoUnit.SECONDS));
}
@@ -1683,18 +1699,18 @@ public class ApplicationApiTest extends ControllerContainerTest {
.build();
}
- private MultiPartStreamer createApplicationDeployData(ApplicationPackage applicationPackage, boolean deployDirectly) {
- return createApplicationDeployData(Optional.of(applicationPackage), deployDirectly);
+ private MultiPartStreamer createApplicationDeployData(ApplicationPackage applicationPackage) {
+ return createApplicationDeployData(Optional.of(applicationPackage));
}
- private MultiPartStreamer createApplicationDeployData(Optional<ApplicationPackage> applicationPackage, boolean deployDirectly) {
- return createApplicationDeployData(applicationPackage, Optional.empty(), deployDirectly);
+ private MultiPartStreamer createApplicationDeployData(Optional<ApplicationPackage> applicationPackage) {
+ return createApplicationDeployData(applicationPackage, Optional.empty());
}
private MultiPartStreamer createApplicationDeployData(Optional<ApplicationPackage> applicationPackage,
- Optional<ApplicationVersion> applicationVersion, boolean deployDirectly) {
+ Optional<ApplicationVersion> applicationVersion) {
MultiPartStreamer streamer = new MultiPartStreamer();
- streamer.addJson("deployOptions", deployOptions(deployDirectly, applicationVersion));
+ streamer.addJson("deployOptions", deployOptions(applicationVersion));
applicationPackage.ifPresent(ap -> streamer.addBytes("applicationZip", ap.zippedContent()));
return streamer;
}
@@ -1706,10 +1722,9 @@ public class ApplicationApiTest extends ControllerContainerTest {
.addBytes(EnvironmentResource.APPLICATION_TEST_ZIP, "content".getBytes());
}
- private String deployOptions(boolean deployDirectly, Optional<ApplicationVersion> applicationVersion) {
+ private String deployOptions(Optional<ApplicationVersion> applicationVersion) {
return "{\"vespaVersion\":null," +
- "\"ignoreValidationErrors\":false," +
- "\"deployDirectly\":" + deployDirectly +
+ "\"ignoreValidationErrors\":false" +
applicationVersion.map(version ->
"," +
"\"buildNumber\":" + version.buildNumber().getAsLong() + "," +
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json
index 0a416600b2c..006c3b98a4d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-athenz.json
@@ -1,7 +1,6 @@
{
"isPublic": false,
"isCd": false,
- "enable-public-signup-flow": (ignore),
"hasTrialCapacity": (ignore),
"user": {
"name": "Joe Developer",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json
index 4e179ad83c5..4ae55e97baa 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-with-applications-cloud.json
@@ -1,7 +1,6 @@
{
"isPublic": true,
"isCd": false,
- "enable-public-signup-flow": (ignore),
"hasTrialCapacity": true,
"user": {
"name": "Joe Developer",
@@ -14,20 +13,17 @@
"roles": [
"developer",
"reader"
- ],
- "enabled-horizon-dashboard":false
+ ]
},
"tenant1": {
"roles": [
"administrator"
- ],
- "enabled-horizon-dashboard":false
+ ]
},
"tenant2": {
"roles": [
"developer"
- ],
- "enabled-horizon-dashboard":false
+ ]
}
},
"flags": [{"id":"enable-public-signup-flow","rules":[{"value":false}]}]
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-applications.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-applications.json
index 7eb445140e7..9f9578e6ed8 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-applications.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-applications.json
@@ -1,7 +1,6 @@
{
"isPublic": (ignore),
"isCd": (ignore),
- "enable-public-signup-flow": (ignore),
"hasTrialCapacity": (ignore),
"user": {
"name": "Joe Developer",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-trial-capacity-cloud.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-trial-capacity-cloud.json
index 3c1edab8cfc..2b98a75068a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-trial-capacity-cloud.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/user-without-trial-capacity-cloud.json
@@ -1,7 +1,6 @@
{
"isPublic": true,
"isCd": false,
- "enable-public-signup-flow": true,
"hasTrialCapacity": false,
"user": {
"name": "Joe Developer",
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java
index f65550bbf0f..e0515a84b5d 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/GetDocumentMessage.java
@@ -16,13 +16,6 @@ public class GetDocumentMessage extends DocumentMessage {
private String fieldSet = DEFAULT_FIELD_SET;
/**
- * Constructs a new message for deserialization.
- */
- GetDocumentMessage() {
- // empty
- }
-
- /**
* Constructs a new document get message.
*
* @param documentId The identifier of the document to get.
diff --git a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java
index b2bf26d3b05..58bc50088d7 100755
--- a/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java
+++ b/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/PutDocumentMessage.java
@@ -19,13 +19,6 @@ public class PutDocumentMessage extends TestAndSetMessage {
private LazyDecoder decoder = null;
/**
- * Constructs a new message for deserialization.
- */
- PutDocumentMessage() {
- // empty
- }
-
- /**
* Constructs a new message from a byte buffer.
*
* @param decoder The decoder to use for deserialization.
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt
index 16dd729b1f1..8a7cd4f66bd 100644
--- a/eval/CMakeLists.txt
+++ b/eval/CMakeLists.txt
@@ -46,6 +46,7 @@ vespa_define_module(
src/tests/gp/ponder_nov2017
src/tests/instruction/add_trivial_dimension_optimizer
src/tests/instruction/dense_dot_product_function
+ src/tests/instruction/dense_hamming_distance
src/tests/instruction/dense_inplace_join_function
src/tests/instruction/dense_matmul_function
src/tests/instruction/dense_multi_matmul_function
diff --git a/eval/src/tests/instruction/dense_hamming_distance/CMakeLists.txt b/eval/src/tests/instruction/dense_hamming_distance/CMakeLists.txt
new file mode 100644
index 00000000000..3d18f9613b3
--- /dev/null
+++ b/eval/src/tests/instruction/dense_hamming_distance/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+vespa_add_executable(eval_dense_hamming_distance_test_app TEST
+ SOURCES
+ dense_hamming_distance_test.cpp
+ DEPENDS
+ vespaeval
+ GTest::GTest
+)
+vespa_add_test(NAME eval_dense_hamming_distance_test_app COMMAND eval_dense_hamming_distance_test_app)
diff --git a/eval/src/tests/instruction/dense_hamming_distance/dense_hamming_distance_test.cpp b/eval/src/tests/instruction/dense_hamming_distance/dense_hamming_distance_test.cpp
new file mode 100644
index 00000000000..8eaa0e72ad5
--- /dev/null
+++ b/eval/src/tests/instruction/dense_hamming_distance/dense_hamming_distance_test.cpp
@@ -0,0 +1,91 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/eval/eval/fast_value.h>
+#include <vespa/eval/eval/tensor_function.h>
+#include <vespa/eval/eval/test/eval_fixture.h>
+#include <vespa/eval/eval/test/gen_spec.h>
+#include <vespa/eval/instruction/dense_hamming_distance.h>
+#include <vespa/vespalib/util/stash.h>
+#include <vespa/vespalib/util/stringfmt.h>
+
+#include <vespa/vespalib/util/require.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+#include <vespa/log/log.h>
+LOG_SETUP("dense_hamming_distance_function_test");
+
+using namespace vespalib;
+using namespace vespalib::eval;
+using namespace vespalib::eval::test;
+
+const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();
+
+struct FunInfo {
+ using LookFor = DenseHammingDistance;
+ void verify(const LookFor &fun) const {
+ EXPECT_TRUE(fun.result_is_mutable());
+ }
+};
+
+void assertOptimized(const vespalib::string &expr) {
+ CellTypeSpace just_int8({CellType::INT8}, 2);
+ EvalFixture::verify<FunInfo>(expr, {FunInfo{}}, just_int8);
+ CellTypeSpace just_double({CellType::DOUBLE}, 2);
+ EvalFixture::verify<FunInfo>(expr, {}, just_double);
+}
+
+void assertNotOptimized(const vespalib::string &expr) {
+ CellTypeSpace just_int8({CellType::INT8}, 2);
+ EvalFixture::verify<FunInfo>(expr, {}, just_int8);
+}
+
+TEST(DenseHammingDistanceOptimizer, hamming_distance_works_with_tensor_function) {
+ assertOptimized("reduce(hamming(x5$1,x5$2),sum)");
+ assertOptimized("reduce(hamming(x5$1,x5$2),sum,x)");
+ assertOptimized("reduce(join(x5$1,x5$2,f(x,y)(hamming(x,y))),sum)");
+ assertOptimized("reduce(join(x5$1,x5$2,f(x,y)(hamming(x,y))),sum,x)");
+}
+
+TEST(DenseHammingDistanceOptimizer, hamming_distance_with_compatible_dimensions_is_optimized) {
+ // various vector sizes
+ assertOptimized("reduce(hamming(x1$1,x1$2),sum)");
+ assertOptimized("reduce(hamming(x3$1,x3$2),sum)");
+ assertOptimized("reduce(hamming(x7$1,x7$2),sum)");
+ assertOptimized("reduce(hamming(x8$1,x8$2),sum)");
+ assertOptimized("reduce(hamming(x9$1,x9$2),sum)");
+ assertOptimized("reduce(hamming(x17$1,x17$2),sum)");
+ // multiple dimensions
+ assertOptimized("reduce(hamming(x3y3$1,x3y3$2),sum)");
+ assertOptimized("reduce(hamming(x3y4$1,x3y4$2),sum)");
+ // with trivial dimensions
+ assertOptimized("reduce(hamming(a1x3$1,x3$2),sum)");
+ assertOptimized("reduce(hamming(x3$1z1,x3$2),sum)");
+ assertOptimized("reduce(hamming(a1x3$1,b1x3$2z1),sum)");
+}
+
+TEST(DenseHammingDistanceOptimizer, hamming_distance_with_mapped_dimensions_is_NOT_optimized) {
+ assertNotOptimized("reduce(hamming(x3_1$1,x3_1$2),sum)");
+ assertNotOptimized("reduce(hamming(x3_1y2$1,x3_1y2$2),sum)");
+}
+
+TEST(DenseHammingDistanceOptimizer, hamming_distance_with_incompatible_dimensions_is_NOT_optimized) {
+ assertNotOptimized("reduce(hamming(x3,y3),sum)");
+ assertNotOptimized("reduce(hamming(y3,x3),sum)");
+ assertNotOptimized("reduce(hamming(x3,x3y3),sum)");
+ assertNotOptimized("reduce(hamming(x3y3,x3),sum)");
+}
+
+TEST(DenseHammingDistanceOptimizer, expressions_similar_to_hamming_distance_are_not_optimized) {
+ assertNotOptimized("reduce(hamming(x3$1,x3$2),prod)");
+}
+
+TEST(DenseHammingDistanceOptimizer, result_must_be_double_to_trigger_optimization) {
+ assertOptimized("reduce(hamming(x3y3$1,x3y3$2),sum,x,y)");
+ assertNotOptimized("reduce(hamming(x3y3$1,x3y3$2),sum,x)");
+ assertNotOptimized("reduce(hamming(x3y3$1,x3y3$2),sum,y)");
+}
+
+//-----------------------------------------------------------------------------
+
+GTEST_MAIN_RUN_ALL_TESTS()
+
diff --git a/eval/src/vespa/eval/eval/optimize_tensor_function.cpp b/eval/src/vespa/eval/eval/optimize_tensor_function.cpp
index 64acbceff04..c2e8d886fde 100644
--- a/eval/src/vespa/eval/eval/optimize_tensor_function.cpp
+++ b/eval/src/vespa/eval/eval/optimize_tensor_function.cpp
@@ -28,6 +28,7 @@
#include <vespa/eval/instruction/vector_from_doubles_function.h>
#include <vespa/eval/instruction/dense_tensor_create_function.h>
#include <vespa/eval/instruction/dense_tensor_peek_function.h>
+#include <vespa/eval/instruction/dense_hamming_distance.h>
#include <vespa/log/log.h>
LOG_SETUP(".eval.eval.optimize_tensor_function");
@@ -53,6 +54,7 @@ const TensorFunction &optimize_for_factory(const ValueBuilderFactory &, const Te
child.set(DenseMatMulFunction::optimize(child.get(), stash));
child.set(DenseMultiMatMulFunction::optimize(child.get(), stash));
child.set(MixedInnerProductFunction::optimize(child.get(), stash));
+ child.set(DenseHammingDistance::optimize(child.get(), stash));
nodes.pop_back();
}
}
diff --git a/eval/src/vespa/eval/instruction/CMakeLists.txt b/eval/src/vespa/eval/instruction/CMakeLists.txt
index 88e3272bb7c..3ed969c0a18 100644
--- a/eval/src/vespa/eval/instruction/CMakeLists.txt
+++ b/eval/src/vespa/eval/instruction/CMakeLists.txt
@@ -5,6 +5,7 @@ vespa_add_library(eval_instruction OBJECT
add_trivial_dimension_optimizer.cpp
dense_cell_range_function.cpp
dense_dot_product_function.cpp
+ dense_hamming_distance.cpp
dense_lambda_peek_function.cpp
dense_lambda_peek_optimizer.cpp
dense_matmul_function.cpp
diff --git a/eval/src/vespa/eval/instruction/dense_hamming_distance.cpp b/eval/src/vespa/eval/instruction/dense_hamming_distance.cpp
new file mode 100644
index 00000000000..2a68663631a
--- /dev/null
+++ b/eval/src/vespa/eval/instruction/dense_hamming_distance.cpp
@@ -0,0 +1,91 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "dense_hamming_distance.h"
+#include <vespa/eval/eval/operation.h>
+#include <vespa/eval/eval/value.h>
+#include <vespa/eval/eval/hamming_distance.h>
+
+#include <vespa/log/log.h>
+LOG_SETUP(".eval.instruction.dense_hamming_distance");
+
+namespace vespalib::eval {
+
+using namespace tensor_function;
+
+namespace {
+
+
+size_t binary_hamming_distance(const void *lhs, const void *rhs, size_t sz) {
+ const uint64_t *words_a = static_cast<const uint64_t *>(lhs);
+ const uint64_t *words_b = static_cast<const uint64_t *>(rhs);
+ size_t sum = 0;
+ size_t i = 0;
+ for (; i * 8 + 7 < sz; ++i) {
+ uint64_t xor_bits = words_a[i] ^ words_b[i];
+ sum += __builtin_popcountl(xor_bits);
+ }
+ if (__builtin_expect((i * 8 < sz), false)) {
+ const uint8_t *bytes_a = static_cast<const uint8_t *>(lhs);
+ const uint8_t *bytes_b = static_cast<const uint8_t *>(rhs);
+ for (i *= 8; i < sz; ++i) {
+ uint64_t xor_bits = bytes_a[i] ^ bytes_b[i];
+ sum += __builtin_popcountl(xor_bits);
+ }
+ }
+ return sum;
+};
+
+void int8_hamming_to_double_op(InterpretedFunction::State &state, uint64_t vector_size) {
+ const auto &lhs = state.peek(1);
+ const auto &rhs = state.peek(0);
+ auto a = lhs.cells();
+ auto b = rhs.cells();
+ double result = binary_hamming_distance(a.data, b.data, vector_size);
+ state.pop_pop_push(state.stash.create<DoubleValue>(result));
+}
+
+bool compatible_types(const ValueType &lhs, const ValueType &rhs) {
+ return ((lhs.cell_type() == CellType::INT8) &&
+ (rhs.cell_type() == CellType::INT8) &&
+ lhs.is_dense() &&
+ rhs.is_dense() &&
+ (lhs.nontrivial_indexed_dimensions() == rhs.nontrivial_indexed_dimensions()));
+}
+
+} // namespace <unnamed>
+
+DenseHammingDistance::DenseHammingDistance(const TensorFunction &lhs_child,
+ const TensorFunction &rhs_child)
+ : tensor_function::Op2(ValueType::double_type(), lhs_child, rhs_child)
+{
+}
+
+InterpretedFunction::Instruction
+DenseHammingDistance::compile_self(const ValueBuilderFactory &, Stash &) const
+{
+ auto op = int8_hamming_to_double_op;
+ const auto &lhs_type = lhs().result_type();
+ const auto &rhs_type = rhs().result_type();
+ LOG_ASSERT(lhs_type.dense_subspace_size() == rhs_type.dense_subspace_size());
+ return InterpretedFunction::Instruction(op, lhs_type.dense_subspace_size());
+}
+
+const TensorFunction &
+DenseHammingDistance::optimize(const TensorFunction &expr, Stash &stash)
+{
+ const auto & res_type = expr.result_type();
+ auto reduce = as<Reduce>(expr);
+ if (res_type.is_double() && reduce && (reduce->aggr() == Aggr::SUM)) {
+ auto join = as<Join>(reduce->child());
+ if (join && (join->function() == operation::Hamming::f)) {
+ const TensorFunction &lhs = join->lhs();
+ const TensorFunction &rhs = join->rhs();
+ if (compatible_types(lhs.result_type(), rhs.result_type())) {
+ return stash.create<DenseHammingDistance>(lhs, rhs);
+ }
+ }
+ }
+ return expr;
+}
+
+} // namespace
diff --git a/eval/src/vespa/eval/instruction/dense_hamming_distance.h b/eval/src/vespa/eval/instruction/dense_hamming_distance.h
new file mode 100644
index 00000000000..efc70d74d21
--- /dev/null
+++ b/eval/src/vespa/eval/instruction/dense_hamming_distance.h
@@ -0,0 +1,22 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/eval/eval/tensor_function.h>
+
+namespace vespalib::eval {
+
+/**
+ * Tensor function for a hamming distance producing a scalar result.
+ **/
+class DenseHammingDistance : public tensor_function::Op2
+{
+public:
+ DenseHammingDistance(const TensorFunction &lhs_child,
+ const TensorFunction &rhs_child);
+ InterpretedFunction::Instruction compile_self(const ValueBuilderFactory &factory, Stash &stash) const override;
+ bool result_is_mutable() const override { return true; }
+ static const TensorFunction &optimize(const TensorFunction &expr, Stash &stash);
+};
+
+} // namespace
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 69d325d4841..28b031e5015 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -79,13 +79,6 @@ public class Flags {
"Takes effect at redeployment",
ZONE_ID, APPLICATION_ID);
- public static final UnboundBooleanFlag ENFORCE_RANK_PROFILE_INHERITANCE = defineFeatureFlag(
- "enforce-rank-profile-inheritance", true,
- List.of("baldersheim"), "2021-09-07", "2021-10-01",
- "Should we enforce verification of rank-profile inheritance.",
- "Takes effect at redeployment",
- ZONE_ID, APPLICATION_ID);
-
public static final UnboundBooleanFlag SKIP_MBUS_REQUEST_THREAD = defineFeatureFlag(
"skip-mbus-request-thread", false,
List.of("baldersheim"), "2020-12-02", "2022-01-01",
@@ -312,6 +305,15 @@ public class Flags {
APPLICATION_ID
);
+ public static final UnboundBooleanFlag ENABLE_TENANT_DEVELOPER_ROLE = defineFeatureFlag(
+ "enable-tenant-developer-role", false,
+ List.of("bjorncs"), "2021-09-23", "2021-12-31",
+ "Enable tenant developer Athenz role in cd/main. Must be set on controller cluster only.",
+ "Takes effect immediately",
+ TENANT_ID
+ );
+
+
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, List<String> owners,
String createdAt, String expiresAt, String description,
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
index 6a733bd8942..487c60e0dcb 100644
--- a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
@@ -340,7 +340,6 @@ public abstract class ControllerHttpClient {
Slime slime = new Slime();
Cursor rootObject = slime.setObject();
deployment.version().ifPresent(version -> rootObject.setString("vespaVersion", version));
- rootObject.setBool("deployDirectly", true);
return toJson(slime);
}
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java b/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java
index b561f08af92..05c3582a541 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java
@@ -63,9 +63,8 @@ public abstract class AbstractResource implements SharedResource {
activeReferences.add(referenceStack);
state = currentStateDebugWithLock();
}
- log.log(Level.WARNING,
- getClass().getName() + "@" + System.identityHashCode(this) + ".refer(): state={ " + state + " }",
- referenceStack);
+ log.log(Level.FINE, referenceStack, () ->
+ getClass().getName() + "@" + System.identityHashCode(this) + ".refer(): state={ " + state + " }");
return new DebugResourceReference(this, referenceStack);
}
@@ -87,9 +86,8 @@ public abstract class AbstractResource implements SharedResource {
}
doDestroy = activeReferences.isEmpty();
}
- log.log(Level.WARNING,
- getClass().getName() + "@" + System.identityHashCode(this) + " release: state={ " + state + " }",
- releaseStack);
+ log.log(Level.FINE, releaseStack,
+ () ->getClass().getName() + "@" + System.identityHashCode(this) + " release: state={ " + state + " }");
if (doDestroy) {
destroy();
}
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java
index c178147a952..a47c6b06321 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ClientProvider.java
@@ -2,7 +2,11 @@
package com.yahoo.jdisc.service;
import com.yahoo.jdisc.Container;
-import com.yahoo.jdisc.application.*;
+import com.yahoo.jdisc.application.BindingRepository;
+import com.yahoo.jdisc.application.ContainerActivator;
+import com.yahoo.jdisc.application.ContainerBuilder;
+import com.yahoo.jdisc.application.Application;
+import com.yahoo.jdisc.application.UriPattern;
import com.yahoo.jdisc.handler.RequestHandler;
/**
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java
index b58f3bc5138..3b5cbfd9cbc 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/service/ServerProvider.java
@@ -37,7 +37,7 @@ public interface ServerProvider extends SharedResource {
* {@link Container} does <em>not</em> call this method, instead it is a required step in the {@link Application}
* initialization code.</p>
*/
- public void start();
+ void start();
/**
* <p>This is a synchronous method to close the listen port (or equivalent) of this ServerProvider and flush any
@@ -48,5 +48,5 @@ public interface ServerProvider extends SharedResource {
* <p>The {@link Container} does <em>not</em> call this method, instead it is a required step in the {@link
* Application} shutdown code.</p>
*/
- public void close();
+ void close();
}
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/NetworkMultiplexer.java b/messagebus/src/main/java/com/yahoo/messagebus/network/NetworkMultiplexer.java
index e75b5d0934a..4bd9bbf7f7f 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/NetworkMultiplexer.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/NetworkMultiplexer.java
@@ -7,10 +7,9 @@ import com.yahoo.text.Utf8Array;
import java.util.Deque;
import java.util.Map;
-import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
/**
@@ -33,15 +32,16 @@ public class NetworkMultiplexer implements NetworkOwner {
private final Network net;
private final Deque<NetworkOwner> owners = new ConcurrentLinkedDeque<>();
private final Map<String, Deque<NetworkOwner>> sessions = new ConcurrentHashMap<>();
- private final boolean shared;
+ private final AtomicBoolean disowned;
private NetworkMultiplexer(Network net, boolean shared) {
net.attach(this);
this.net = net;
- this.shared = shared;
+ this.disowned = new AtomicBoolean( ! shared);
}
- /** Returns a network multiplexer which will be shared between several {@link NetworkOwner}s. */
+ /** Returns a network multiplexer which will be shared between several {@link NetworkOwner}s,
+ * and will shut down when all these have detached, and {@link #disown()} has been called, in any order. */
public static NetworkMultiplexer shared(Network net) {
return new NetworkMultiplexer(net, true);
}
@@ -100,6 +100,7 @@ public class NetworkMultiplexer implements NetworkOwner {
owner.deliverMessage(message, session);
}
+ /** Attach the network owner to this, allowing this to forward messages to it. */
public void attach(NetworkOwner owner) {
if (owners.contains(owner))
throw new IllegalArgumentException(owner + " is already attached to this");
@@ -107,23 +108,27 @@ public class NetworkMultiplexer implements NetworkOwner {
owners.add(owner);
}
+ /** Detach the network owner from this, no longer allowing messages to it, and shutting down this is ownerless. */
public void detach(NetworkOwner owner) {
if ( ! owners.remove(owner))
throw new IllegalArgumentException(owner + " not attached to this");
- if ( ! shared && owners.isEmpty())
- net.shutdown();
+ destroyIfOwnerless();
}
- public void destroy() {
- if ( ! shared)
- throw new UnsupportedOperationException("Destroy called on a dedicated multiplexer; " +
- "this automatically shuts down when detached from");
+ /** Signal that external ownership of this is relinquished, allowing destruction on last owner detachment. */
+ public void disown() {
+ if (disowned.getAndSet(true))
+ throw new IllegalStateException("Destroy called on a dedicated multiplexer--" +
+ "this automatically shuts down when detached from--or " +
+ "called multiple times on a shared multiplexer");
- if ( ! owners.isEmpty())
- log.warning("NetworkMultiplexer destroyed before all owners detached: " + this);
+ destroyIfOwnerless();
+ }
- net.shutdown();
+ private void destroyIfOwnerless() {
+ if (disowned.get() && owners.isEmpty())
+ net.shutdown();
}
public Network net() {
@@ -136,7 +141,7 @@ public class NetworkMultiplexer implements NetworkOwner {
"net=" + net +
", owners=" + owners +
", sessions=" + sessions +
- ", shared=" + shared +
+ ", destructible=" + disowned +
'}';
}
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/NamedRPCService.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/NamedRPCService.java
new file mode 100644
index 00000000000..59cafed1836
--- /dev/null
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/NamedRPCService.java
@@ -0,0 +1,48 @@
+package com.yahoo.messagebus.network.rpc;
+
+import com.yahoo.jrt.slobrok.api.IMirror;
+import com.yahoo.jrt.slobrok.api.Mirror;
+
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class NamedRPCService implements RPCService {
+ private final IMirror mirror;
+ private final String pattern;
+ private int addressIdx = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
+ private int addressGen = 0;
+ private List<Mirror.Entry> addressList = null;
+
+ /**
+ * Create a new RPCService backed by the given network and using the given service pattern.
+ *
+ * @param mirror The naming server to send queries to.
+ * @param pattern The pattern to use when querying.
+ */
+ public NamedRPCService(IMirror mirror, String pattern) {
+ this.mirror = mirror;
+ this.pattern = pattern;
+ }
+
+ /**
+ * Resolve a concrete address from this service. This service may represent multiple remote sessions, so this will
+ * select one that is online.
+ *
+ * @return A concrete service address.
+ */
+ public synchronized RPCServiceAddress resolve() {
+ if (addressGen != mirror.updates()) {
+ addressGen = mirror.updates();
+ addressList = mirror.lookup(pattern);
+ }
+ if (addressList != null && !addressList.isEmpty()) {
+ ++addressIdx;
+ if (addressIdx >= addressList.size()) {
+ addressIdx = 0;
+ }
+ Mirror.Entry entry = addressList.get(addressIdx);
+ return new RPCServiceAddress(entry.getName(), entry.getSpec());
+ }
+ return null;
+ }
+}
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCService.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCService.java
index fb3c4cf9971..889df32ce1e 100644
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCService.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCService.java
@@ -2,10 +2,6 @@
package com.yahoo.messagebus.network.rpc;
import com.yahoo.jrt.slobrok.api.IMirror;
-import com.yahoo.jrt.slobrok.api.Mirror;
-
-import java.util.List;
-import java.util.concurrent.ThreadLocalRandom;
/**
* An RPCService represents a set of remote sessions matching a service pattern. The sessions are monitored using the
@@ -13,23 +9,13 @@ import java.util.concurrent.ThreadLocalRandom;
*
* @author havardpe
*/
-public class RPCService {
-
- private final IMirror mirror;
- private final String pattern;
- private int addressIdx = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
- private int addressGen = 0;
- private List<Mirror.Entry> addressList = null;
+public interface RPCService {
- /**
- * Create a new RPCService backed by the given network and using the given service pattern.
- *
- * @param mirror The naming server to send queries to.
- * @param pattern The pattern to use when querying.
- */
- public RPCService(IMirror mirror, String pattern) {
- this.mirror = mirror;
- this.pattern = pattern;
+ static RPCService create(IMirror mirror, String pattern) {
+ if (pattern.startsWith("tcp/")) {
+ return new TcpRPCService(pattern);
+ }
+ return new NamedRPCService(mirror, pattern);
}
/**
@@ -38,38 +24,6 @@ public class RPCService {
*
* @return A concrete service address.
*/
- public RPCServiceAddress resolve() {
- if (pattern.startsWith("tcp/")) {
- int pos = pattern.lastIndexOf('/');
- if (pos > 0 && pos < pattern.length() - 1) {
- RPCServiceAddress ret = new RPCServiceAddress(pattern, pattern.substring(0, pos));
- if (!ret.isMalformed()) {
- return ret;
- }
- }
- } else {
- if (addressGen != mirror.updates()) {
- addressGen = mirror.updates();
- addressList = mirror.lookup(pattern);
- }
- if (addressList != null && !addressList.isEmpty()) {
- ++addressIdx;
- if (addressIdx >= addressList.size()) {
- addressIdx = 0;
- }
- Mirror.Entry entry = addressList.get(addressIdx);
- return new RPCServiceAddress(entry.getName(), entry.getSpec());
- }
- }
- return null;
- }
+ RPCServiceAddress resolve();
- /**
- * Returns the pattern used when querying for the naming server for addresses. This is given at construtor time.
- *
- * @return The service pattern.
- */
- String getPattern() {
- return pattern;
- }
}
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServiceAddress.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServiceAddress.java
index 0a6a58d4e89..1b7bcf01731 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServiceAddress.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServiceAddress.java
@@ -34,6 +34,13 @@ public class RPCServiceAddress implements ServiceAddress {
public RPCServiceAddress(String serviceName, String connectionSpec) {
this(serviceName, new Spec(connectionSpec));
}
+ public RPCServiceAddress(RPCServiceAddress blueprint) {
+ serviceName = blueprint.serviceName;
+ sessionName = blueprint.sessionName;
+ connectionSpec = blueprint.connectionSpec;
+ target = null;
+ }
+
@Override
public boolean equals(Object obj) {
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java
index abd33d6c9c2..a666a03c401 100755
--- a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/RPCServicePool.java
@@ -1,6 +1,8 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.messagebus.network.rpc;
+import com.yahoo.concurrent.CopyOnWriteHashMap;
+
import java.util.LinkedHashMap;
import java.util.Map;
@@ -12,7 +14,7 @@ import java.util.Map;
public class RPCServicePool {
private final RPCNetwork net;
- private final ThreadLocalCache services = new ThreadLocalCache();
+ private final Map<Long, ServiceLRUCache> mapOfServiceCache;
private final int maxSize;
/**
@@ -23,6 +25,7 @@ public class RPCServicePool {
*/
public RPCServicePool(RPCNetwork net, int maxSize) {
this.net = net;
+ mapOfServiceCache = new CopyOnWriteHashMap<>();
this.maxSize = maxSize;
}
@@ -34,12 +37,12 @@ public class RPCServicePool {
* @return A service address for the given pattern.
*/
public RPCServiceAddress resolve(String pattern) {
- RPCService service = services.get().get(pattern);
- if (service == null) {
- service = new RPCService(net.getMirror(), pattern);
- services.get().put(pattern, service);
- }
- return service.resolve();
+
+ return getPerThreadCache().computeIfAbsent(pattern, (key) -> RPCService.create(net.getMirror(), key)).resolve();
+ }
+
+ private ServiceLRUCache getPerThreadCache() {
+ return mapOfServiceCache.computeIfAbsent(Thread.currentThread().getId(), (key) -> new ServiceLRUCache(maxSize));
}
/**
@@ -49,7 +52,7 @@ public class RPCServicePool {
* @return The current size of this pool.
*/
public int getSize() {
- return services.get().size();
+ return getPerThreadCache().size();
}
/**
@@ -59,21 +62,15 @@ public class RPCServicePool {
* @return True if a corresponding service is in the pool.
*/
public boolean hasService(String pattern) {
- return services.get().containsKey(pattern);
- }
-
- private class ThreadLocalCache extends ThreadLocal<ServiceLRUCache> {
-
- @Override
- protected ServiceLRUCache initialValue() {
- return new ServiceLRUCache();
- }
+ return getPerThreadCache().containsKey(pattern);
}
- private class ServiceLRUCache extends LinkedHashMap<String, RPCService> {
+ private static class ServiceLRUCache extends LinkedHashMap<String, RPCService> {
+ private final int maxSize;
- ServiceLRUCache() {
+ ServiceLRUCache(int maxSize) {
super(16, 0.75f, true);
+ this.maxSize = maxSize;
}
@Override
diff --git a/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/TcpRPCService.java b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/TcpRPCService.java
new file mode 100644
index 00000000000..e2fae59b429
--- /dev/null
+++ b/messagebus/src/main/java/com/yahoo/messagebus/network/rpc/TcpRPCService.java
@@ -0,0 +1,23 @@
+package com.yahoo.messagebus.network.rpc;
+
+public class TcpRPCService implements RPCService {
+ private final RPCServiceAddress blueprint;
+
+ TcpRPCService(String pattern) {
+ if ( ! pattern.startsWith("tcp/")) {
+ throw new IllegalArgumentException("Expect tcp adress to start with 'tcp/', was: " + pattern);
+ }
+ RPCServiceAddress ret = null;
+ int pos = pattern.lastIndexOf('/');
+ if (pos > 0 && pos < pattern.length() - 1) {
+ ret = new RPCServiceAddress(pattern, pattern.substring(0, pos));
+ if ( ret.isMalformed()) {
+ ret = null;
+ }
+ }
+ blueprint = ret;
+ }
+ public RPCServiceAddress resolve() {
+ return blueprint != null ? new RPCServiceAddress(blueprint) : null;
+ }
+}
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/network/NetworkMultiplexerTest.java b/messagebus/src/test/java/com/yahoo/messagebus/network/NetworkMultiplexerTest.java
index 3bf754f29c7..6451a692fb6 100644
--- a/messagebus/src/test/java/com/yahoo/messagebus/network/NetworkMultiplexerTest.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/network/NetworkMultiplexerTest.java
@@ -86,7 +86,11 @@ public class NetworkMultiplexerTest {
shared.detach(owner2);
assertFalse(net.shutDown.get());
- shared.destroy();
+ shared.attach(owner2);
+ shared.disown();
+ assertFalse(net.shutDown.get());
+
+ shared.detach(owner2);
assertTrue(net.shutDown.get());
}
diff --git a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java
index 1dbb30de585..0343b075579 100755
--- a/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java
+++ b/messagebus/src/test/java/com/yahoo/messagebus/network/rpc/ServiceAddressTestCase.java
@@ -83,11 +83,11 @@ public class ServiceAddressTestCase {
}
private void assertNullAddress(String pattern) {
- assertNull(new RPCService(network.getMirror(), pattern).resolve());
+ assertNull(RPCService.create(network.getMirror(), pattern).resolve());
}
private void assertAddress(String pattern, String expectedSpec, String expectedSession) {
- RPCService service = new RPCService(network.getMirror(), pattern);
+ RPCService service = RPCService.create(network.getMirror(), pattern);
RPCServiceAddress obj = service.resolve();
assertNotNull(obj);
assertNotNull(obj.getConnectionSpec());
diff --git a/searchcore/CMakeLists.txt b/searchcore/CMakeLists.txt
index c0353747e80..36ef347b7ac 100644
--- a/searchcore/CMakeLists.txt
+++ b/searchcore/CMakeLists.txt
@@ -54,6 +54,7 @@ vespa_define_module(
src/apps/vespa-transactionlog-inspect
TESTS
+ src/tests/bmcluster/estimate_moved_docs_ratio
src/tests/grouping
src/tests/proton/attribute
src/tests/proton/attribute/attribute_aspect_delayer
diff --git a/searchcore/src/apps/vespa-redistribute-bm/vespa_redistribute_bm.cpp b/searchcore/src/apps/vespa-redistribute-bm/vespa_redistribute_bm.cpp
index e5c3959d2d4..0227d9539d2 100644
--- a/searchcore/src/apps/vespa-redistribute-bm/vespa_redistribute_bm.cpp
+++ b/searchcore/src/apps/vespa-redistribute-bm/vespa_redistribute_bm.cpp
@@ -17,6 +17,8 @@
#include <vespa/searchcore/bmcluster/bm_node_stats_reporter.h>
#include <vespa/searchcore/bmcluster/bm_range.h>
#include <vespa/searchcore/bmcluster/bucket_selector.h>
+#include <vespa/searchcore/bmcluster/calculate_moved_docs_ratio.h>
+#include <vespa/searchcore/bmcluster/estimate_moved_docs_ratio.h>
#include <vespa/searchcore/bmcluster/spi_bm_feed_handler.h>
#include <vespa/searchlib/index/dummyfileheadercontext.h>
#include <vespa/vespalib/io/fileutil.h>
@@ -51,6 +53,8 @@ using search::bmcluster::BmNode;
using search::bmcluster::BmNodeStatsReporter;
using search::bmcluster::BmRange;
using search::bmcluster::BucketSelector;
+using search::bmcluster::CalculateMovedDocsRatio;
+using search::bmcluster::EstimateMovedDocsRatio;
using search::index::DummyFileHeaderContext;
using storage::lib::State;
@@ -100,76 +104,6 @@ vespalib::string& get_mode_name(Mode mode) {
return (i < mode_names.size()) ? mode_names[i] : bad_mode_name;
}
-double
-estimate_lost_docs_base_ratio(uint32_t redundancy, uint32_t lost_nodes, uint32_t num_nodes)
-{
- if (redundancy > lost_nodes) {
- return 0.0;
- }
- double loss_ratio = 1.0;
- for (uint32_t i = 0; i < redundancy; ++i) {
- loss_ratio *= ((double) (lost_nodes - i)) / (num_nodes - i);
- }
- LOG(info, "estimated lost docs base ratio: %4.2f", loss_ratio);
- return loss_ratio;
-}
-
-double
-estimate_moved_docs_ratio_grow(uint32_t redundancy, uint32_t added_nodes, uint32_t num_nodes)
-{
- double new_redundancy = redundancy;
- double new_per_node_doc_ratio = new_redundancy / num_nodes;
- double moved_ratio = new_per_node_doc_ratio * added_nodes;
- LOG(info, "estimated_moved_docs_ratio_grow(%u,%u,%u)=%4.2f", redundancy, added_nodes, num_nodes, moved_ratio);
- return moved_ratio;
-}
-
-double
-estimate_moved_docs_ratio_shrink(uint32_t redundancy, uint32_t retired_nodes, uint32_t num_nodes)
-{
- double old_redundancy = redundancy;
- double old_per_node_doc_ratio = old_redundancy / num_nodes;
- uint32_t new_nodes = num_nodes - retired_nodes;
- double new_redundancy = std::min(redundancy, new_nodes);
- double new_per_node_doc_ratio = new_redundancy / new_nodes;
- double moved_ratio = (new_per_node_doc_ratio - old_per_node_doc_ratio) * new_nodes;
- LOG(info, "estimated_moved_docs_ratio_shrink(%u,%u,%u)=%4.2f", redundancy, retired_nodes, num_nodes, moved_ratio);
- return moved_ratio;
-}
-
-double
-estimate_moved_docs_ratio_crash(uint32_t redundancy, uint32_t crashed_nodes, uint32_t num_nodes)
-{
- double old_redundancy = redundancy;
- double old_per_node_doc_ratio = old_redundancy / num_nodes;
- uint32_t new_nodes = num_nodes - crashed_nodes;
- double new_redundancy = std::min(redundancy, new_nodes);
- double new_per_node_doc_ratio = new_redundancy / new_nodes;
- double lost_docs_ratio = estimate_lost_docs_base_ratio(redundancy, crashed_nodes, num_nodes) * new_redundancy;
- double moved_ratio = (new_per_node_doc_ratio - old_per_node_doc_ratio) * new_nodes - lost_docs_ratio;
- LOG(info, "estimated_moved_docs_ratio_crash(%u,%u,%u)=%4.2f", redundancy, crashed_nodes, num_nodes, moved_ratio);
- return moved_ratio;
-}
-
-double
-estimate_moved_docs_ratio_replace(uint32_t redundancy, uint32_t added_nodes, uint32_t retired_nodes, uint32_t num_nodes)
-{
- uint32_t old_nodes = num_nodes - added_nodes;
- double old_redundancy = std::min(redundancy, old_nodes);
- double old_per_node_doc_ratio = old_redundancy / old_nodes;
- uint32_t new_nodes = num_nodes - retired_nodes;
- double new_redundancy = std::min(redundancy, new_nodes);
- double new_per_node_doc_ratio = new_redundancy / new_nodes;
- double moved_ratio = new_per_node_doc_ratio * added_nodes;
- uint32_t stable_nodes = num_nodes - added_nodes - retired_nodes;
- // Account for extra documents moved from retired nodes to stable nodes
- double extra_per_stable_node_doc_ratio = new_per_node_doc_ratio * added_nodes / old_nodes;
- double extra_moved_ratio = (std::min(1.0, new_per_node_doc_ratio + extra_per_stable_node_doc_ratio) - old_per_node_doc_ratio) * stable_nodes;
- moved_ratio += extra_moved_ratio;
- LOG(info, "estimated_moved_docs_ratio_replace(%u,%u,%u,%u)=%4.2f, (of which %4.2f extra)", redundancy, added_nodes, retired_nodes, num_nodes, moved_ratio, extra_moved_ratio);
- return moved_ratio;
-}
-
class BMParams : public BmClusterParams,
public BmFeedParams
{
@@ -391,7 +325,7 @@ Benchmark::estimate_lost_docs()
case Mode::TEMP_CRASH:
{
double new_redundancy = std::min(_params.get_redundancy(), _params.get_num_nodes() - _params.get_flip_nodes());
- auto lost_docs_ratio = estimate_lost_docs_base_ratio(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_num_nodes()) * new_redundancy;
+ auto lost_docs_ratio = EstimateMovedDocsRatio().estimate_lost_docs_base_ratio(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_num_nodes()) * new_redundancy;
return _params.get_documents() * lost_docs_ratio;
}
default:
@@ -404,14 +338,21 @@ Benchmark::estimate_moved_docs()
{
switch(_params.get_mode()) {
case Mode::GROW:
- return _params.get_documents() * estimate_moved_docs_ratio_grow(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_num_nodes());
+ return _params.get_documents() * EstimateMovedDocsRatio().estimate_moved_docs_ratio_grow(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_num_nodes());
case Mode::SHRINK:
- return _params.get_documents() * estimate_moved_docs_ratio_shrink(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_num_nodes());
+ return _params.get_documents() * EstimateMovedDocsRatio().estimate_moved_docs_ratio_shrink(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_num_nodes());
case Mode::PERM_CRASH:
case Mode::TEMP_CRASH:
- return _params.get_documents() * estimate_moved_docs_ratio_crash(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_num_nodes());
+ return _params.get_documents() * EstimateMovedDocsRatio().estimate_moved_docs_ratio_crash(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_num_nodes());
case Mode::REPLACE:
- return _params.get_documents() * estimate_moved_docs_ratio_replace(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_flip_nodes(), _params.get_num_nodes());
+ if (_params.get_num_nodes() < 10) {
+ // Calculate better estimate for moved docs ratio with brute force
+ auto scanner = CalculateMovedDocsRatio::make_replace_calculator(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_flip_nodes(), _params.get_num_nodes());
+ scanner.scan();
+ return _params.get_documents() * scanner.get_moved_docs_ratio();
+ } else {
+ return _params.get_documents() * EstimateMovedDocsRatio().estimate_moved_docs_ratio_replace(_params.get_redundancy(), _params.get_flip_nodes(), _params.get_flip_nodes(), _params.get_num_nodes());
+ }
default:
return 0.0;
}
diff --git a/searchcore/src/tests/bmcluster/estimate_moved_docs_ratio/CMakeLists.txt b/searchcore/src/tests/bmcluster/estimate_moved_docs_ratio/CMakeLists.txt
new file mode 100644
index 00000000000..23f72e4f1fa
--- /dev/null
+++ b/searchcore/src/tests/bmcluster/estimate_moved_docs_ratio/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchcore_bmcluster_estimate_moved_docs_ratio_test_app TEST
+ SOURCES
+ estimate_moved_docs_ratio_test.cpp
+ DEPENDS
+ searchcore_bmcluster
+ GTest::GTest
+)
+
+vespa_add_test(NAME searchcore_bmcluster_estimate_moved_docs_ratio_test_app
+ COMMAND searchcore_bmcluster_estimate_moved_docs_ratio_test_app)
diff --git a/searchcore/src/tests/bmcluster/estimate_moved_docs_ratio/estimate_moved_docs_ratio_test.cpp b/searchcore/src/tests/bmcluster/estimate_moved_docs_ratio/estimate_moved_docs_ratio_test.cpp
new file mode 100644
index 00000000000..79af31e3247
--- /dev/null
+++ b/searchcore/src/tests/bmcluster/estimate_moved_docs_ratio/estimate_moved_docs_ratio_test.cpp
@@ -0,0 +1,134 @@
+// 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/searchcore/bmcluster/calculate_moved_docs_ratio.h>
+#include <vespa/searchcore/bmcluster/estimate_moved_docs_ratio.h>
+#include <iostream>
+
+#include <vespa/log/log.h>
+LOG_SETUP("estimate_moved_docs_ratio_test");
+
+using search::bmcluster::CalculateMovedDocsRatio;
+using search::bmcluster::EstimateMovedDocsRatio;
+
+namespace {
+
+bool verbose;
+
+TEST(EstimateMovedDocsRatioTest, estimate_lost_docs_ratio)
+{
+ for (uint32_t nodes = 1; nodes < 2; ++nodes) {
+ for (uint32_t redundancy = 1; redundancy <= nodes; ++redundancy) {
+ for (uint32_t lost_nodes = 0; lost_nodes <= nodes; ++lost_nodes) {
+ auto scanner = CalculateMovedDocsRatio::make_crash_calculator(redundancy, lost_nodes, nodes);
+ scanner.scan();
+ double lost_docs_base_ratio = scanner.get_lost_docs_base_ratio();
+ double estimated_lost_docs_base_ratio = EstimateMovedDocsRatio().estimate_lost_docs_base_ratio(redundancy, lost_nodes, nodes);
+ EXPECT_DOUBLE_EQ(lost_docs_base_ratio, estimated_lost_docs_base_ratio);
+ }
+ }
+ }
+}
+
+TEST(EstimateMovedDocsRatioTest, estimate_moved_docs_ratio_grow)
+{
+ for (uint32_t nodes = 1; nodes < 10; ++nodes) {
+ for (uint32_t redundancy = 1; redundancy <= nodes; ++redundancy) {
+ for (uint32_t added_nodes = 0; added_nodes <= nodes; ++added_nodes) {
+ auto scanner = CalculateMovedDocsRatio::make_grow_calculator(redundancy, added_nodes, nodes);
+ scanner.scan();
+ double moved_docs_ratio = scanner.get_moved_docs_ratio();
+ double estimated_moved_docs_ratio = EstimateMovedDocsRatio().estimate_moved_docs_ratio_grow(redundancy, added_nodes, nodes);
+ EXPECT_DOUBLE_EQ(moved_docs_ratio, estimated_moved_docs_ratio);
+ }
+ }
+ }
+}
+
+TEST(EstimateMovedDocsRatioTest, estimate_moved_docs_ratio_shrink)
+{
+ for (uint32_t nodes = 1; nodes < 10; ++nodes) {
+ for (uint32_t redundancy = 1; redundancy <= nodes; ++redundancy) {
+ for (uint32_t retired_nodes = 0; retired_nodes <= nodes; ++retired_nodes) {
+ auto scanner = CalculateMovedDocsRatio::make_shrink_calculator(redundancy, retired_nodes, nodes);
+ scanner.scan();
+ double moved_docs_ratio = scanner.get_moved_docs_ratio();
+ double estimated_moved_docs_ratio = EstimateMovedDocsRatio().estimate_moved_docs_ratio_shrink(redundancy, retired_nodes, nodes);
+ EXPECT_DOUBLE_EQ(moved_docs_ratio, estimated_moved_docs_ratio);
+ }
+ }
+ }
+}
+
+TEST(EstimateMovedDocsRatioTest, estimate_moved_docs_ratio_crash)
+{
+ double epsilon = 1e-15;
+ for (uint32_t nodes = 1; nodes < 10; ++nodes) {
+ for (uint32_t redundancy = 1; redundancy <= nodes; ++redundancy) {
+ for (uint32_t crashed_nodes = 0; crashed_nodes <= nodes; ++crashed_nodes) {
+ auto scanner = CalculateMovedDocsRatio::make_crash_calculator(redundancy, crashed_nodes, nodes);
+ scanner.scan();
+ double moved_docs_ratio = scanner.get_moved_docs_ratio();
+ double estimated_moved_docs_ratio = EstimateMovedDocsRatio().estimate_moved_docs_ratio_crash(redundancy, crashed_nodes, nodes);
+ EXPECT_NEAR(moved_docs_ratio, estimated_moved_docs_ratio, epsilon);
+ }
+ }
+ }
+}
+
+TEST(EstimateMovedDocsRatioTest, estimate_moved_docs_ratio_replace)
+{
+ uint32_t bad_cases = 0;
+ uint32_t really_bad_cases = 0;
+ if (verbose) {
+ std::cout << "Summary: HDR Red A Ret N Act Est ScaleMv ScaleEs States" << std::endl;
+ }
+ for (uint32_t nodes = 1; nodes < 6; ++nodes) {
+ for (uint32_t redundancy = 1; redundancy <= nodes; ++redundancy) {
+ for (uint32_t retired_nodes = 0; retired_nodes <= nodes; ++retired_nodes) {
+ for (uint32_t added_nodes = 0; added_nodes <= nodes - retired_nodes; ++added_nodes) {
+ // std::cout << "Estimate moved docs ratio replace " << retired_nodes << " of " << nodes << " retired, added " << added_nodes << " nodes ,redundancy " << redundancy << std::endl;
+ auto scanner = CalculateMovedDocsRatio::make_replace_calculator(redundancy, added_nodes, retired_nodes, nodes);
+ scanner.scan();
+ double moved_docs_ratio = scanner.get_moved_docs_ratio();
+ double estimated_moved_docs_ratio = EstimateMovedDocsRatio(verbose).estimate_moved_docs_ratio_replace(redundancy, added_nodes, retired_nodes, nodes);
+ double error_ratio = abs(moved_docs_ratio - estimated_moved_docs_ratio);
+ bool bad = error_ratio > 1e-8;
+ bool really_bad = error_ratio > 0.2 * estimated_moved_docs_ratio + 1e-8;
+ if (bad) {
+ ++bad_cases;
+ }
+ if (really_bad) {
+ ++really_bad_cases;
+ }
+ if (verbose) {
+ double scaled_moved = moved_docs_ratio * scanner.get_checked_states();
+ double scaled_estimated_moved = estimated_moved_docs_ratio * scanner.get_checked_states();
+ std::cout << "Summary: " << (bad ? "BAD" : "OK ") << std::setw(4) << redundancy << std::setw(4) << added_nodes << std::setw(4) << retired_nodes << std::setw(4) << nodes << " " << std::setw(12) << std::setprecision(5) << std::fixed << moved_docs_ratio << " " << std::setw(12) << std::setprecision(5) << std::fixed << estimated_moved_docs_ratio << " " << std::setw(12) << std::setprecision(5) << std::fixed << scaled_moved << " " << std::setw(12) << std::setprecision(5) << std::fixed << scaled_estimated_moved << std::setw(8) << scanner.get_checked_states();
+ std::cout << " [";
+ for (uint32_t node_idx = 0; node_idx < nodes; ++node_idx) {
+ std::cout << std::setw(8) << scanner.get_moved_docs_per_node()[node_idx];
+ }
+ std::cout << " ]" << std::endl;
+ }
+ // TODO: Fix calculation so we get zero bad cases.
+ // EXPECT_DOUBLE_EQ(moved_docs_ratio, estimated_moved_docs_ratio);
+ }
+ }
+ }
+ }
+ EXPECT_LE(6, bad_cases);
+ EXPECT_LE(1, really_bad_cases);
+}
+
+} // namespace
+
+int
+main(int argc, char* argv[])
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ if (argc > 1 && std::string("--verbose") == argv[1]) {
+ verbose = true;
+ }
+ return RUN_ALL_TESTS();
+}
diff --git a/searchcore/src/tests/proton/server/disk_mem_usage_sampler/disk_mem_usage_sampler_test.cpp b/searchcore/src/tests/proton/server/disk_mem_usage_sampler/disk_mem_usage_sampler_test.cpp
index b7f217290d6..1475ac7f9d7 100644
--- a/searchcore/src/tests/proton/server/disk_mem_usage_sampler/disk_mem_usage_sampler_test.cpp
+++ b/searchcore/src/tests/proton/server/disk_mem_usage_sampler/disk_mem_usage_sampler_test.cpp
@@ -62,7 +62,13 @@ TEST_F(DiskMemUsageSamplerTest, resource_usage_is_sampled)
std::this_thread::sleep_for(50ms);
}
LOG(info, "Polled %zu times (%zu ms) to get a sample", i, i * 50);
+#ifdef __linux__
+ // Anonymous resident memory used by current process is sampled.
EXPECT_GT(filter().getMemoryStats().getAnonymousRss(), 0);
+#else
+ // Anonymous resident memory used by current process is not sampled.
+ EXPECT_EQ(filter().getMemoryStats().getAnonymousRss(), 0);
+#endif
EXPECT_GT(filter().getDiskUsedSize(), 0);
EXPECT_EQ(100, filter().get_transient_memory_usage());
EXPECT_EQ(100.0 / memory_size_bytes, filter().get_relative_transient_memory_usage());
diff --git a/searchcore/src/vespa/searchcore/bmcluster/CMakeLists.txt b/searchcore/src/vespa/searchcore/bmcluster/CMakeLists.txt
index d2ab8b0c2a9..0de021a9514 100644
--- a/searchcore/src/vespa/searchcore/bmcluster/CMakeLists.txt
+++ b/searchcore/src/vespa/searchcore/bmcluster/CMakeLists.txt
@@ -19,7 +19,9 @@ vespa_add_library(searchcore_bmcluster STATIC
bm_storage_link.cpp
bm_storage_message_addresses.cpp
bucket_info_queue.cpp
+ calculate_moved_docs_ratio.cpp
document_api_message_bus_bm_feed_handler.cpp
+ estimate_moved_docs_ratio.cpp
pending_tracker.cpp
pending_tracker_hash.cpp
spi_bm_feed_handler.cpp
diff --git a/searchcore/src/vespa/searchcore/bmcluster/bm_cluster_params.cpp b/searchcore/src/vespa/searchcore/bmcluster/bm_cluster_params.cpp
index 2a214130392..f31f5fb6b88 100644
--- a/searchcore/src/vespa/searchcore/bmcluster/bm_cluster_params.cpp
+++ b/searchcore/src/vespa/searchcore/bmcluster/bm_cluster_params.cpp
@@ -6,7 +6,7 @@
namespace search::bmcluster {
BmClusterParams::BmClusterParams()
- : _bucket_db_stripe_bits(0),
+ : _bucket_db_stripe_bits(4),
_distributor_stripes(0),
_enable_distributor(false),
_enable_service_layer(false),
diff --git a/searchcore/src/vespa/searchcore/bmcluster/bm_node_stats_reporter.cpp b/searchcore/src/vespa/searchcore/bmcluster/bm_node_stats_reporter.cpp
index 83956dfc274..ecd0593031e 100644
--- a/searchcore/src/vespa/searchcore/bmcluster/bm_node_stats_reporter.cpp
+++ b/searchcore/src/vespa/searchcore/bmcluster/bm_node_stats_reporter.cpp
@@ -95,16 +95,16 @@ BmNodeStatsReporter::report()
for (auto &node : node_stats) {
auto& document_db = node.get_document_db_stats();
if (document_db.has_value()) {
- s << Width(8) << document_db.value().get_total_docs();
+ s << Width(10) << document_db.value().get_total_docs();
} else {
- s << Width(8) << "-";
+ s << Width(10) << "-";
}
totals += node;
}
if (totals.get_document_db_stats().has_value()) {
- s << Width(8) << totals.get_document_db_stats().value().get_total_docs();
+ s << Width(10) << totals.get_document_db_stats().value().get_total_docs();
} else {
- s << Width(8) << "-";
+ s << Width(10) << "-";
}
auto& total_buckets = totals.get_buckets_stats();
if (total_buckets.has_value()) {
diff --git a/searchcore/src/vespa/searchcore/bmcluster/calculate_moved_docs_ratio.cpp b/searchcore/src/vespa/searchcore/bmcluster/calculate_moved_docs_ratio.cpp
new file mode 100644
index 00000000000..63f23476a95
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/bmcluster/calculate_moved_docs_ratio.cpp
@@ -0,0 +1,142 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "calculate_moved_docs_ratio.h"
+#include <cassert>
+
+namespace search::bmcluster {
+
+namespace {
+
+uint32_t make_bit_range(uint32_t low, uint32_t high)
+{
+ return (1u << high) - (1u << low);
+}
+
+}
+
+struct CalculateMovedDocsRatio::Placements
+{
+ uint32_t _mask;
+ uint32_t _count;
+
+ Placements()
+ : _mask(0u),
+ _count(0u)
+ {
+ }
+
+ Placements(uint32_t mask, uint32_t count) noexcept
+ : _mask(mask),
+ _count(count)
+ {
+ }
+
+ Placements add(uint32_t idx) const noexcept {
+ return Placements(_mask | (1u << idx), _count + 1);
+ }
+
+ Placements add(uint32_t idx, uint32_t mask, uint32_t redundancy) const noexcept {
+ return (((_count < redundancy) && ((1u << idx) & mask) != 0)) ? Placements(_mask | (1u << idx), _count + 1) : *this;
+ }
+};
+
+
+CalculateMovedDocsRatio::CalculateMovedDocsRatio(uint32_t nodes, uint32_t redundancy, uint32_t old_placement_mask, uint32_t new_placement_mask, uint32_t new_up_mask)
+ : _num_states(nodes + 1),
+ _nodes(nodes),
+ _old_placement_mask(old_placement_mask),
+ _new_placement_mask(new_placement_mask),
+ _new_up_mask(new_up_mask),
+ _moved_docs(0u),
+ _moved_docs_per_node(nodes),
+ _checked_states(0u),
+ _lost_docs_base(0u),
+ _redundancy(redundancy),
+ _old_redundancy(std::min(redundancy, (uint32_t)__builtin_popcount(old_placement_mask))),
+ _new_redundancy(std::min(redundancy, (uint32_t)__builtin_popcount(new_placement_mask)))
+{
+ assert((new_placement_mask & ~new_up_mask) == 0u);
+ uint32_t states = 1;
+ for (uint32_t level = nodes; level > 0; --level) {
+ states *= std::max(1u, nodes - level);
+ _num_states[level] = states;
+ }
+ _num_states[0] = states * nodes;
+}
+
+CalculateMovedDocsRatio::~CalculateMovedDocsRatio() = default;
+
+CalculateMovedDocsRatio
+CalculateMovedDocsRatio::make_grow_calculator(uint32_t redundancy, uint32_t added_nodes, uint32_t nodes)
+{
+ uint32_t old_placement_mask = make_bit_range(added_nodes, nodes);
+ uint32_t new_placement_mask = make_bit_range(0, nodes);
+ return CalculateMovedDocsRatio(nodes, redundancy, old_placement_mask, new_placement_mask, new_placement_mask);
+}
+
+CalculateMovedDocsRatio
+CalculateMovedDocsRatio::make_shrink_calculator(uint32_t redundancy, uint32_t retired_nodes, uint32_t nodes)
+{
+ uint32_t old_placement_mask = make_bit_range(0, nodes);
+ uint32_t new_placement_mask = make_bit_range(retired_nodes, nodes);
+ return CalculateMovedDocsRatio(nodes, redundancy, old_placement_mask, new_placement_mask, old_placement_mask);
+}
+
+CalculateMovedDocsRatio
+CalculateMovedDocsRatio::make_crash_calculator(uint32_t redundancy, uint32_t crashed_nodes, uint32_t nodes)
+{
+ uint32_t old_placement_mask = make_bit_range(0, nodes);
+ uint32_t new_placement_mask = make_bit_range(crashed_nodes, nodes);
+ return CalculateMovedDocsRatio(nodes, redundancy, old_placement_mask, new_placement_mask, new_placement_mask);
+
+}
+
+CalculateMovedDocsRatio
+CalculateMovedDocsRatio::make_replace_calculator(uint32_t redundancy, uint32_t added_nodes, uint32_t retired_nodes, uint32_t nodes)
+{
+ uint32_t old_placement_mask = make_bit_range(added_nodes, nodes);
+ uint32_t new_placement_mask = make_bit_range(added_nodes + retired_nodes, nodes) | make_bit_range(0, added_nodes);
+ uint32_t new_up_mask = make_bit_range(0, nodes);
+ return CalculateMovedDocsRatio(nodes, redundancy, old_placement_mask, new_placement_mask, new_up_mask);
+}
+
+void
+CalculateMovedDocsRatio::scan(Placements selected, Placements old_placement, Placements new_placement)
+{
+ if (old_placement._count >= _old_redundancy) {
+ if ((old_placement._mask & _new_up_mask) == 0) {
+ _lost_docs_base += _num_states[selected._count];
+ _checked_states += _num_states[selected._count];
+ return;
+ }
+ if (new_placement._count >= _new_redundancy) {
+ _checked_states += _num_states[selected._count];
+ uint32_t only_new_mask = new_placement._mask & ~old_placement._mask;
+ if (only_new_mask != 0) {
+ _moved_docs += _num_states[selected._count] * (uint32_t)__builtin_popcount(only_new_mask);
+ for (uint32_t node_idx = 0; node_idx < _nodes; ++node_idx) {
+ if ((only_new_mask & (1u << node_idx)) != 0) {
+ _moved_docs_per_node[node_idx] += _num_states[selected._count];
+ }
+ }
+ }
+ return;
+ }
+ }
+ assert(selected._count < _nodes);
+ for (uint32_t node_idx = 0; node_idx < _nodes; ++node_idx) {
+ if ((selected._mask & (1u << node_idx)) != 0) {
+ continue;
+ }
+ scan(selected.add(node_idx), old_placement.add(node_idx, _old_placement_mask, _old_redundancy), new_placement.add(node_idx, _new_placement_mask, _new_redundancy));
+ }
+}
+
+void
+CalculateMovedDocsRatio::scan()
+{
+ scan(Placements(), Placements(), Placements());
+ assert(_checked_states == _num_states[0]);
+}
+
+}
diff --git a/searchcore/src/vespa/searchcore/bmcluster/calculate_moved_docs_ratio.h b/searchcore/src/vespa/searchcore/bmcluster/calculate_moved_docs_ratio.h
new file mode 100644
index 00000000000..cd161177246
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/bmcluster/calculate_moved_docs_ratio.h
@@ -0,0 +1,48 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+namespace search::bmcluster {
+
+/*
+ * Class for calculating moved docs ratio during
+ * document redistribution.
+ */
+class CalculateMovedDocsRatio
+{
+ class Placements;
+ std::vector<uint32_t> _num_states;
+ uint32_t _nodes;
+ uint32_t _old_placement_mask;
+ uint32_t _new_placement_mask;
+ uint32_t _new_up_mask;
+ uint32_t _moved_docs;
+ std::vector<uint32_t> _moved_docs_per_node;
+ uint32_t _checked_states;
+ uint32_t _lost_docs_base;
+ uint32_t _redundancy;
+ uint32_t _old_redundancy;
+ uint32_t _new_redundancy;
+
+ void scan(Placements selected, Placements old_placement, Placements new_placement);
+public:
+ CalculateMovedDocsRatio(uint32_t nodes, uint32_t redundancy, uint32_t old_placement_mask, uint32_t new_placement_mask, uint32_t new_up_mask);
+ ~CalculateMovedDocsRatio();
+ static CalculateMovedDocsRatio make_grow_calculator(uint32_t redundancy, uint32_t added_nodes, uint32_t nodes);
+ static CalculateMovedDocsRatio make_shrink_calculator(uint32_t redundancy, uint32_t retired_nodes, uint32_t nodes);
+ static CalculateMovedDocsRatio make_crash_calculator(uint32_t redundancy, uint32_t crashed_nodes, uint32_t nodes);
+ static CalculateMovedDocsRatio make_replace_calculator(uint32_t redundancy, uint32_t added_nodes, uint32_t retired_nodes, uint32_t nodes);
+ void scan();
+ uint32_t get_lost_docs_base() const noexcept { return _lost_docs_base; }
+ uint32_t get_checked_states() const noexcept { return _checked_states; }
+ uint32_t get_new_redundancy() const noexcept { return _new_redundancy; }
+ uint32_t get_moved_docs() const noexcept { return _moved_docs; }
+ const std::vector<uint32_t>& get_moved_docs_per_node() const noexcept { return _moved_docs_per_node; }
+ double get_lost_docs_base_ratio() const noexcept { return ((double) _lost_docs_base) / _checked_states; }
+ double get_moved_docs_ratio() const noexcept { return ((double) _moved_docs) / _checked_states; }
+};
+
+}
diff --git a/searchcore/src/vespa/searchcore/bmcluster/estimate_moved_docs_ratio.cpp b/searchcore/src/vespa/searchcore/bmcluster/estimate_moved_docs_ratio.cpp
new file mode 100644
index 00000000000..cffa6fc79e9
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/bmcluster/estimate_moved_docs_ratio.cpp
@@ -0,0 +1,114 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "estimate_moved_docs_ratio.h"
+
+#include <vespa/log/log.h>
+LOG_SETUP(".bmcluster.estimate_moved_docs_ratio");
+
+namespace search::bmcluster {
+
+EstimateMovedDocsRatio::EstimateMovedDocsRatio()
+ : EstimateMovedDocsRatio(false)
+{
+}
+
+EstimateMovedDocsRatio::EstimateMovedDocsRatio(bool verbose)
+ : _verbose(verbose)
+{
+}
+
+double
+EstimateMovedDocsRatio::estimate_lost_docs_base_ratio(uint32_t redundancy, uint32_t lost_nodes, uint32_t num_nodes)
+{
+ if (redundancy > lost_nodes) {
+ return 0.0;
+ }
+ double loss_ratio = 1.0;
+ for (uint32_t i = 0; i < redundancy; ++i) {
+ loss_ratio *= ((double) (lost_nodes - i)) / (num_nodes - i);
+ }
+ if (_verbose) {
+ LOG(info, "estimated lost docs base ratio: %4.2f", loss_ratio);
+ }
+ return loss_ratio;
+}
+
+double
+EstimateMovedDocsRatio::estimate_moved_docs_ratio_grow(uint32_t redundancy, uint32_t added_nodes, uint32_t num_nodes)
+{
+ if (added_nodes == num_nodes) {
+ return 0.0;
+ }
+ double new_redundancy = redundancy;
+ double new_per_node_doc_ratio = new_redundancy / num_nodes;
+ double moved_ratio = new_per_node_doc_ratio * added_nodes;
+ if (_verbose) {
+ LOG(info, "estimated_moved_docs_ratio_grow(%u,%u,%u)=%4.2f", redundancy, added_nodes, num_nodes, moved_ratio);
+ }
+ return moved_ratio;
+}
+
+double
+EstimateMovedDocsRatio::estimate_moved_docs_ratio_shrink(uint32_t redundancy, uint32_t retired_nodes, uint32_t num_nodes)
+{
+ if (retired_nodes == num_nodes) {
+ return 0.0;
+ }
+ double old_redundancy = redundancy;
+ double old_per_node_doc_ratio = old_redundancy / num_nodes;
+ uint32_t new_nodes = num_nodes - retired_nodes;
+ double new_redundancy = std::min(redundancy, new_nodes);
+ double new_per_node_doc_ratio = new_redundancy / new_nodes;
+ double moved_ratio = (new_per_node_doc_ratio - old_per_node_doc_ratio) * new_nodes;
+ if (_verbose) {
+ LOG(info, "estimated_moved_docs_ratio_shrink(%u,%u,%u)=%4.2f", redundancy, retired_nodes, num_nodes, moved_ratio);
+ }
+ return moved_ratio;
+}
+
+double
+EstimateMovedDocsRatio::estimate_moved_docs_ratio_crash(uint32_t redundancy, uint32_t crashed_nodes, uint32_t num_nodes)
+{
+ if (crashed_nodes == num_nodes) {
+ return 0.0;
+ }
+ double old_redundancy = redundancy;
+ double old_per_node_doc_ratio = old_redundancy / num_nodes;
+ uint32_t new_nodes = num_nodes - crashed_nodes;
+ double new_redundancy = std::min(redundancy, new_nodes);
+ double new_per_node_doc_ratio = new_redundancy / new_nodes;
+ double lost_docs_ratio = estimate_lost_docs_base_ratio(redundancy, crashed_nodes, num_nodes) * new_redundancy;
+ double moved_ratio = (new_per_node_doc_ratio - old_per_node_doc_ratio) * new_nodes - lost_docs_ratio;
+ if (_verbose) {
+ LOG(info, "estimated_moved_docs_ratio_crash(%u,%u,%u)=%4.2f", redundancy, crashed_nodes, num_nodes, moved_ratio);
+ }
+ return moved_ratio;
+}
+
+double
+EstimateMovedDocsRatio::estimate_moved_docs_ratio_replace(uint32_t redundancy, uint32_t added_nodes, uint32_t retired_nodes, uint32_t num_nodes)
+{
+ if (added_nodes == num_nodes || retired_nodes == num_nodes) {
+ return 0.0;
+ }
+ uint32_t old_nodes = num_nodes - added_nodes;
+ double old_redundancy = std::min(redundancy, old_nodes);
+ [[maybe_unused]] double old_per_node_doc_ratio = old_redundancy / old_nodes;
+ uint32_t new_nodes = num_nodes - retired_nodes;
+ double new_redundancy = std::min(redundancy, new_nodes);
+ double new_per_node_doc_ratio = new_redundancy / new_nodes;
+ double moved_ratio = new_per_node_doc_ratio * added_nodes;
+ uint32_t stable_nodes = num_nodes - added_nodes - retired_nodes;
+ // Account for extra documents moved from retired nodes to stable nodes
+ // TODO: Fix calculation
+ double baseline_per_node_doc_ratio = ((double) redundancy) / num_nodes;
+ double extra_per_stable_node_doc_ratio = std::min(baseline_per_node_doc_ratio * retired_nodes / new_nodes, 1.0 - old_per_node_doc_ratio);
+ double extra_moved_ratio = extra_per_stable_node_doc_ratio * stable_nodes;
+ moved_ratio += extra_moved_ratio;
+ if (_verbose) {
+ LOG(info, "estimated_moved_docs_ratio_replace(%u,%u,%u,%u)=%4.2f, (of which %4.2f extra)", redundancy, added_nodes, retired_nodes, num_nodes, moved_ratio, extra_moved_ratio);
+ }
+ return moved_ratio;
+}
+
+}
diff --git a/searchcore/src/vespa/searchcore/bmcluster/estimate_moved_docs_ratio.h b/searchcore/src/vespa/searchcore/bmcluster/estimate_moved_docs_ratio.h
new file mode 100644
index 00000000000..fb14e80098d
--- /dev/null
+++ b/searchcore/src/vespa/searchcore/bmcluster/estimate_moved_docs_ratio.h
@@ -0,0 +1,25 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <cstdint>
+
+namespace search::bmcluster {
+
+/*
+ * Class for estimating moved docs ratio during
+ * document redistribution.
+ */
+class EstimateMovedDocsRatio {
+ bool _verbose;
+public:
+ EstimateMovedDocsRatio();
+ explicit EstimateMovedDocsRatio(bool verbose);
+ double estimate_lost_docs_base_ratio(uint32_t redundancy, uint32_t lost_nodes, uint32_t num_nodes);
+ double estimate_moved_docs_ratio_grow(uint32_t redundancy, uint32_t added_nodes, uint32_t num_nodes);
+ double estimate_moved_docs_ratio_shrink(uint32_t redundancy, uint32_t retired_nodes, uint32_t num_nodes);
+ double estimate_moved_docs_ratio_crash(uint32_t redundancy, uint32_t crashed_nodes, uint32_t num_nodes);
+ double estimate_moved_docs_ratio_replace(uint32_t redundancy, uint32_t added_nodes, uint32_t retired_nodes, uint32_t num_nodes);
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp b/searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp
index 0a13b196c43..90369009f0e 100644
--- a/searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp
+++ b/searchlib/src/vespa/searchlib/fef/blueprintresolver.cpp
@@ -3,8 +3,10 @@
#include "blueprintresolver.h"
#include "blueprintfactory.h"
#include "featurenameparser.h"
+#include <vespa/vespalib/util/lambdatask.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/size_literals.h>
+#include <vespa/vespalib/util/threadstackexecutor.h>
#include <stack>
#include <cassert>
#include <set>
@@ -14,6 +16,8 @@
LOG_SETUP(".fef.blueprintresolver");
using vespalib::make_string_short::fmt;
+using vespalib::ThreadStackExecutor;
+using vespalib::makeLambdaTask;
namespace search::fef {
@@ -271,8 +275,7 @@ BlueprintResolver::compile()
{
assert(_executorSpecs.empty()); // only one compilation allowed
Compiler compiler(_factory, _indexEnv, _executorSpecs, _featureMap);
- std::thread compile_thread([&]()
- {
+ auto compile_task = makeLambdaTask([&]() {
compiler.probe_stack();
for (const auto &seed: _seeds) {
auto ref = compiler.resolve_feature(seed, Blueprint::AcceptInput::ANY);
@@ -282,7 +285,10 @@ BlueprintResolver::compile()
_seedMap.emplace(FeatureNameParser(seed).featureName(), ref);
}
});
- compile_thread.join();
+ ThreadStackExecutor executor(1, 8_Mi);
+ executor.execute(std::move(compile_task));
+ executor.sync();
+ executor.shutdown();
size_t stack_usage = compiler.stack_usage();
if (stack_usage > (128_Ki)) {
LOG(warning, "high stack usage: %zu bytes", stack_usage);
diff --git a/slobrok/src/tests/rpc_mapping_monitor/rpc_mapping_monitor_test.cpp b/slobrok/src/tests/rpc_mapping_monitor/rpc_mapping_monitor_test.cpp
index f503453f934..0edcde172aa 100644
--- a/slobrok/src/tests/rpc_mapping_monitor/rpc_mapping_monitor_test.cpp
+++ b/slobrok/src/tests/rpc_mapping_monitor/rpc_mapping_monitor_test.cpp
@@ -148,7 +148,7 @@ TEST_F(RpcMappingMonitorTest, hurry_means_faster) {
EXPECT_TRUE(debugger.step_until([&]() {
return ((hist.map[foo_a].samples() > 0)); }));
auto t2 = debugger.time();
- fprintf(stderr, "hurry: ~%zu ms, normal: ~%zu ms\n", count_ms(t1-t0), count_ms(t2-t0));
+ fprintf(stderr, "hurry: ~%" PRIu64 " ms, normal: ~%" PRIu64 " ms\n", count_ms(t1-t0), count_ms(t2-t0));
EXPECT_GT((t2 - t0), 10 * (t1 - t0));
EXPECT_EQ(hist.map[foo_a].state(), State::UP);
EXPECT_EQ(hist.map[baz_b].state(), State::UP);
@@ -218,7 +218,7 @@ TEST_F(RpcMappingMonitorTest, detect_ping_interval) {
a.set_last_conn(nullptr);
EXPECT_TRUE(debugger.step_until([&]() { return (a.last_conn); }));
auto t2 = debugger.time();
- fprintf(stderr, "ping interval: ~%zu ms\n", count_ms(t2-t1));
+ fprintf(stderr, "ping interval: ~%" PRIu64 " ms\n", count_ms(t2-t1));
}
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java b/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java
index dc50db1e598..a28d8be8f57 100644
--- a/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java
+++ b/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java
@@ -24,6 +24,7 @@ import java.util.stream.Collectors;
* @author ollivir
*/
public class LocalFileDb implements FileAcquirer, FileRegistry {
+
private final Map<FileReference, File> fileReferenceToFile = new HashMap<>();
private final Path appPath;
@@ -37,7 +38,7 @@ public class LocalFileDb implements FileAcquirer, FileRegistry {
synchronized (this) {
File file = fileReferenceToFile.get(reference);
if (file == null) {
- return new File(reference.value()); // Downloaded file reference: Will (hopefully) be resolved client side
+ return new File(reference.value()); // Downloaded file reference: Will (hopefully) be resolved client side
}
return file;
}
@@ -48,6 +49,7 @@ public class LocalFileDb implements FileAcquirer, FileRegistry {
}
/* FileRegistry overrides */
+ @Override
public FileReference addFile(String relativePath) {
File file = appPath.resolve(relativePath).toFile();
if (!file.exists()) {
diff --git a/standalone-container/src/main/sh/standalone-container.sh b/standalone-container/src/main/sh/standalone-container.sh
index b30a7ad4cd0..e6cbb435b6f 100755
--- a/standalone-container/src/main/sh/standalone-container.sh
+++ b/standalone-container/src/main/sh/standalone-container.sh
@@ -180,7 +180,6 @@ StartCommand() {
-Djdisc.config.file="$cfpfile" \
-Djdisc.export.packages= \
-Djdisc.cache.path="$bundlecachedir" \
- -Djdisc.debug.resources=false \
-Djdisc.bundle.path="$VESPA_HOME/lib/jars" \
-Djdisc.logger.enabled=true \
-Djdisc.logger.level=ALL \
diff --git a/storage/src/tests/distributor/distributor_stripe_test.cpp b/storage/src/tests/distributor/distributor_stripe_test.cpp
index 418b224eb25..7fdc2578545 100644
--- a/storage/src/tests/distributor/distributor_stripe_test.cpp
+++ b/storage/src/tests/distributor/distributor_stripe_test.cpp
@@ -230,6 +230,25 @@ TEST_F(DistributorStripeTest, operations_generated_and_started_without_duplicate
ASSERT_EQ(6, _sender.commands().size());
}
+TEST_F(DistributorStripeTest, maintenance_scheduling_inhibited_if_cluster_state_is_pending)
+{
+ setup_stripe(Redundancy(2), NodeCount(4), "storage:3 distributor:1");
+ simulate_set_pending_cluster_state("storage:4 distributor:1");
+
+ _sender.commands().clear(); // Remove pending bucket info requests
+
+ tickDistributorNTimes(1);
+ EXPECT_FALSE(stripe_is_in_recovery_mode());
+
+ for (uint32_t i = 0; i < 6; ++i) {
+ addNodesToBucketDB(document::BucketId(16, i), "0=2"); // Needs activation, merging
+ }
+ tickDistributorNTimes(10);
+
+ // No ops should have been actually generated
+ ASSERT_EQ(0, _sender.commands().size());
+}
+
TEST_F(DistributorStripeTest, recovery_mode_on_cluster_state_change)
{
setup_stripe(Redundancy(1), NodeCount(2),
diff --git a/vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClient.java b/vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClient.java
index 0089499701f..6fea2d3faa4 100644
--- a/vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClient.java
+++ b/vespa-feed-client/src/main/java/ai/vespa/feed/client/FeedClient.java
@@ -22,18 +22,19 @@ public interface FeedClient extends Closeable {
/**
* Send a document put with the given parameters, returning a future with the result of the operation.
* Exceptional completion will use be an instance of {@link FeedException} or one of its sub-classes.
- * */
+ */
CompletableFuture<Result> put(DocumentId documentId, String documentJson, OperationParameters params);
/**
* Send a document update with the given parameters, returning a future with the result of the operation.
* Exceptional completion will use be an instance of {@link FeedException} or one of its sub-classes.
- * */
+ */
CompletableFuture<Result> update(DocumentId documentId, String updateJson, OperationParameters params);
- /** Send a document remove with the given parameters, returning a future with the result of the operation.
- * Exceptional completion will use be an instance of {@link FeedException} or one of its sub-classes.
- * */
+ /**
+ * Send a document remove with the given parameters, returning a future with the result of the operation.
+ * Exceptional completion will use be an instance of {@link FeedException} or one of its sub-classes.
+ */
CompletableFuture<Result> remove(DocumentId documentId, OperationParameters params);
/** Returns a snapshot of the stats for this feed client, such as requests made, and responses by status. */
diff --git a/vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpRequestStrategy.java b/vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpRequestStrategy.java
index 58112f86090..345177dcf36 100644
--- a/vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpRequestStrategy.java
+++ b/vespa-feed-client/src/main/java/ai/vespa/feed/client/HttpRequestStrategy.java
@@ -163,9 +163,9 @@ class HttpRequestStrategy implements RequestStrategy {
return true;
}
- breaker.failure(response);
logResponse(FINE, response, request, attempt);
if (response.code() == 500 || response.code() == 502 || response.code() == 504) { // Hopefully temporary errors.
+ breaker.failure(response);
return retry(request, attempt);
}
diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java
index 50bdde28180..89597a2352a 100644
--- a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java
+++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java
@@ -108,7 +108,10 @@ public abstract class AbstractVespaMojo extends AbstractMojo {
private Optional<Path> apiKeyPath(String tenant) {
if (!isNullOrBlank(apiKeyFile)) return Optional.of(Paths.get(apiKeyFile));
- Path cliApiKeyFile = Paths.get(System.getProperty("user.home"), ".vespa", tenant + ".api-key.pem");
+ Path cliApiKeyFile = Optional.ofNullable(System.getenv("VESPA_CLI_HOME"))
+ .map(Paths::get)
+ .orElseGet(() -> Paths.get(System.getProperty("user.home"), ".vespa"))
+ .resolve(tenant + ".api-key.pem");
if (Files.exists(cliApiKeyFile)) return Optional.of(cliApiKeyFile);
return Optional.empty();
diff --git a/vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java b/vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java
index 857b7cc6acd..98370a8735a 100644
--- a/vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java
+++ b/vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java
@@ -9,7 +9,7 @@ import java.util.Map;
import java.util.Set;
/**
- * A hashmap wrapper which defers cloning of the enclosed map until it is written.
+ * A hashmap wrapper which defers cloning of the enclosed map until it is written to.
* Use this to make clones cheap in maps which are often not further modified.
* <p>
* As with regular maps, this can only be used safely if the content of the map is immutable.
diff --git a/vespajlib/src/main/java/com/yahoo/slime/ArrayValue.java b/vespajlib/src/main/java/com/yahoo/slime/ArrayValue.java
index c8a7f2253bb..ac1a33eea0d 100644
--- a/vespajlib/src/main/java/com/yahoo/slime/ArrayValue.java
+++ b/vespajlib/src/main/java/com/yahoo/slime/ArrayValue.java
@@ -1,7 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.slime;
-
final class ArrayValue extends Value {
private int capacity = 16;
@@ -10,16 +9,16 @@ final class ArrayValue extends Value {
private final SymbolTable names;
public ArrayValue(SymbolTable names) { this.names = names; }
- public final Type type() { return Type.ARRAY; }
- public final int children() { return used; }
- public final int entries() { return used; }
- public final Value entry(int index) {
+ public Type type() { return Type.ARRAY; }
+ public int children() { return used; }
+ public int entries() { return used; }
+ public Value entry(int index) {
return (index < used) ? values[index] : NixValue.invalid();
}
- public final void accept(Visitor v) { v.visitArray(this); }
+ public void accept(Visitor v) { v.visitArray(this); }
- public final void traverse(ArrayTraverser at) {
+ public void traverse(ArrayTraverser at) {
for (int i = 0; i < used; i++) {
at.entry(i, values[i]);
}
@@ -32,7 +31,7 @@ final class ArrayValue extends Value {
System.arraycopy(v, 0, values, 0, used);
}
- protected final Value addLeaf(Value value) {
+ protected Value addLeaf(Value value) {
if (used == capacity) {
grow();
}
@@ -40,6 +39,7 @@ final class ArrayValue extends Value {
return value;
}
- public final Value addArray() { return addLeaf(new ArrayValue(names)); }
- public final Value addObject() { return addLeaf(new ObjectValue(names)); }
+ public Value addArray() { return addLeaf(new ArrayValue(names)); }
+ public Value addObject() { return addLeaf(new ObjectValue(names)); }
+
}