public async Task MoveLoadingNodeToLast() { var clusterSize = 3; var settings = new Dictionary <string, string>() { [RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout)] = 300.ToString(), [RavenConfiguration.GetKey(x => x.Cluster.StabilizationTime)] = "1", [RavenConfiguration.GetKey(x => x.Cluster.MoveToRehabGraceTime)] = "5", [RavenConfiguration.GetKey(x => x.Cluster.RotatePreferredNodeGraceTime)] = "1", [RavenConfiguration.GetKey(x => x.Replication.ReplicationMinimalHeartbeat)] = "15", }; var cluster = await CreateRaftCluster(clusterSize, false, 0, watcherCluster : true, customSettings : settings); using (var store = GetDocumentStore(new Options { Server = cluster.Leader, ReplicationFactor = clusterSize })) { var tcs = new TaskCompletionSource <DocumentDatabase>(); var databaseName = store.Database; using (var session = store.OpenSession()) { session.Store(new User { Name = "Karmel" }, "users/1"); session.SaveChanges(); Assert.True(await WaitForDocumentInClusterAsync <User>(cluster.Nodes, databaseName, "users/1", _ => true, TimeSpan.FromSeconds(5))); } var record = await store.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(store.Database)); var preferred = Servers.Single(s => s.ServerStore.NodeTag == record.Topology.Members[0]); int val; using (new DisposableAction(() => { preferred.ServerStore.DatabasesLandlord.DatabasesCache.TryRemove(databaseName, out var t); if (t == tcs.Task) { tcs.SetCanceled(); } })) { var t = preferred.ServerStore.DatabasesLandlord.DatabasesCache.Replace(databaseName, tcs.Task); t.Result.Dispose(); Assert.True(await WaitForValueAsync(async() => { record = await store.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(store.Database)); return(record.Topology.Members[0] != preferred.ServerStore.NodeTag); }, true)); val = await WaitForValueAsync(async() => await GetRehabCount(store, databaseName), 1); Assert.Equal(1, val); val = await WaitForValueAsync(async() => await GetMembersCount(store, databaseName), clusterSize - 1); Assert.Equal(clusterSize - 1, val); } val = await WaitForValueAsync(async() => await GetRehabCount(store, databaseName), 0); Assert.Equal(0, val); val = await WaitForValueAsync(async() => await GetMembersCount(store, databaseName), 2); Assert.Equal(clusterSize, val); } }
public async Task ShouldKeepPullingDocsAfterServerRestart() { var dataPath = NewDataPath(); IDocumentStore store = null; RavenServer server = null; SubscriptionWorker <dynamic> subscriptionWorker = null; try { server = GetNewServer(runInMemory: false, customSettings: new Dictionary <string, string>() { [RavenConfiguration.GetKey(x => x.Core.DataDirectory)] = dataPath }); store = new DocumentStore() { Urls = new[] { server.ServerStore.GetNodeHttpServerUrl() }, Database = "RavenDB_2627", }.Initialize(); var doc = new DatabaseRecord(store.Database); var result = store.Maintenance.Server.Send(new CreateDatabaseOperationWithoutNameValidation(doc)); await WaitForRaftIndexToBeAppliedInCluster(result.RaftCommandIndex, _reasonableWaitTime); using (var session = store.OpenSession()) { session.Store(new User()); session.Store(new User()); session.Store(new User()); session.Store(new User()); session.SaveChanges(); } var id = store.Subscriptions.Create(new SubscriptionCreationOptions <User>()); subscriptionWorker = store.Subscriptions.GetSubscriptionWorker(new SubscriptionWorkerOptions(id) { TimeToWaitBeforeConnectionRetry = TimeSpan.FromSeconds(1), MaxDocsPerBatch = 1 }); var gotBatch = new ManualResetEventSlim(); var gotArek = new ManualResetEventSlim(); var t = subscriptionWorker.Run(x => { gotBatch.Set(); foreach (var item in x.Items) { if (item.Id == "users/arek") { gotArek.Set(); } } }); Assert.True(gotBatch.Wait(_reasonableWaitTime)); Server.ServerStore.DatabasesLandlord.UnloadDirectly(store.Database); for (int i = 0; i < 150; i++) { try { using (var session = store.OpenSession()) { session.Store(new User(), "users/arek"); session.SaveChanges(); } break; } catch { Thread.Sleep(25); if (i > 100) { throw; } } } Assert.True(gotArek.Wait(_reasonableWaitTime)); } finally { subscriptionWorker?.Dispose(); store?.Dispose(); server.Dispose(); } }
private void ValidateLocalRootPath() { if (LocalRootPath == null) { return; } var directoryInfo = new DirectoryInfo(LocalRootPath.FullPath); if (directoryInfo.Exists == false) { throw new ArgumentException($"The backup path '{LocalRootPath.FullPath}' defined in the configuration under '{RavenConfiguration.GetKey(x => x.Backup.LocalRootPath)}' doesn't exist."); } }
protected virtual DocumentStore GetDocumentStore(Options options = null, [CallerMemberName] string caller = null) { try { lock (_getDocumentStoreSync) { options = options ?? Options.Default; var serverToUse = options.Server ?? Server; var name = GetDatabaseName(caller); if (options.ModifyDatabaseName != null) { name = options.ModifyDatabaseName(name) ?? name; } var hardDelete = true; var runInMemory = options.RunInMemory; var pathToUse = options.Path; if (pathToUse == null) { pathToUse = NewDataPath(name); } else { hardDelete = false; runInMemory = false; } var doc = new DatabaseRecord(name) { Settings = { [RavenConfiguration.GetKey(x => x.Replication.ReplicationMinimalHeartbeat)] = "1", [RavenConfiguration.GetKey(x => x.Replication.RetryReplicateAfter)] = "1", [RavenConfiguration.GetKey(x => x.Core.RunInMemory)] = runInMemory.ToString(), [RavenConfiguration.GetKey(x => x.Core.DataDirectory)] = pathToUse, [RavenConfiguration.GetKey(x => x.Core.ThrowIfAnyIndexCannotBeOpened)] = "true", [RavenConfiguration.GetKey(x => x.Indexing.MinNumberOfMapAttemptsAfterWhichBatchWillBeCanceledIfRunningLowOnMemory)] = int.MaxValue.ToString(), } }; if (options.Encrypted) { SetupForEncryptedDatabase(options, name, serverToUse, doc); } options.ModifyDatabaseRecord?.Invoke(doc); var store = new DocumentStore { Urls = UseFiddler(serverToUse.WebUrl), Database = name, Certificate = options.ClientCertificate }; options.ModifyDocumentStore?.Invoke(store); //This gives too much error details in most cases, we don't need this now store.RequestExecutorCreated += (sender, executor) => { executor.AdditionalErrorInformation += sb => sb.AppendLine().Append(GetLastStatesFromAllServersOrderedByTime()); }; store.Initialize(); if (options.CreateDatabase) { if (IsGlobalOrLocalServer(serverToUse)) { CheckIfDatabaseExists(serverToUse, name); } else { Servers.ForEach(server => CheckIfDatabaseExists(server, name)); } DatabasePutResult result; if (options.AdminCertificate != null) { using (var adminStore = new DocumentStore { Urls = UseFiddler(serverToUse.WebUrl), Database = name, Certificate = options.AdminCertificate }.Initialize()) { result = adminStore.Maintenance.Server.Send(new CreateDatabaseOperation(doc, options.ReplicationFactor)); } } else { result = store.Maintenance.Server.Send(new CreateDatabaseOperation(doc, options.ReplicationFactor)); } Assert.True(result.RaftCommandIndex > 0); //sanity check if (IsGlobalOrLocalServer(serverToUse)) { // skip 'wait for requests' on DocumentDatabase dispose ApplySkipDrainAllRequestsToDatabase(serverToUse, name); } else { var timeout = TimeSpan.FromMinutes(Debugger.IsAttached ? 5 : 1); AsyncHelpers.RunSync(async() => await WaitForRaftIndexToBeAppliedInCluster(result.RaftCommandIndex, timeout)); // skip 'wait for requests' on DocumentDatabase dispose Servers.ForEach(server => ApplySkipDrainAllRequestsToDatabase(server, name)); } } store.BeforeDispose += (sender, args) => { if (CreatedStores.TryRemove(store) == false) { return; // can happen if we are wrapping the store inside sharded one } DeleteDatabaseResult result = null; if (options.DeleteDatabaseOnDispose) { result = DeleteDatabase(options, serverToUse, name, hardDelete, store); } if (IsGlobalOrLocalServer(serverToUse) == false && result != null) { var timeout = TimeSpan.FromMinutes(Debugger.IsAttached ? 5 : 1); AsyncHelpers.RunSync(async() => await WaitForRaftIndexToBeAppliedInCluster(result.RaftCommandIndex, timeout)); } }; CreatedStores.Add(store); return(store); } } catch (TimeoutException te) { throw new TimeoutException($"{te.Message} {Environment.NewLine} {te.StackTrace}{Environment.NewLine}Servers states:{Environment.NewLine}{GetLastStatesFromAllServersOrderedByTime()}"); } }
public Task SetupUnsecured() { AssertOnlyInSetupMode(); using (ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context)) using (var setupInfoJson = context.ReadForMemory(RequestBodyStream(), "setup-unsecured")) { // Making sure we don't have leftovers from previous setup try { using (var tx = context.OpenWriteTransaction()) { ServerStore.Engine.DeleteTopology(context); tx.Commit(); } } catch (Exception) { // ignored } var setupInfo = JsonDeserializationServer.UnsecuredSetupInfo(setupInfoJson); BlittableJsonReaderObject settingsJson; using (var fs = new FileStream(ServerStore.Configuration.ConfigPath, FileMode.Open, FileAccess.Read)) { settingsJson = context.ReadForMemory(fs, "settings-json"); } settingsJson.Modifications = new DynamicJsonValue(settingsJson) { [RavenConfiguration.GetKey(x => x.Licensing.EulaAccepted)] = true, [RavenConfiguration.GetKey(x => x.Core.SetupMode)] = nameof(SetupMode.Unsecured), [RavenConfiguration.GetKey(x => x.Security.UnsecuredAccessAllowed)] = nameof(UnsecuredAccessAddressRange.PublicNetwork) }; if (setupInfo.Port == 0) { setupInfo.Port = 8080; } settingsJson.Modifications[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = string.Join(";", setupInfo.Addresses.Select(ip => IpAddressToUrl(ip, setupInfo.Port))); if (setupInfo.TcpPort == 0) { setupInfo.TcpPort = 38888; } settingsJson.Modifications[RavenConfiguration.GetKey(x => x.Core.TcpServerUrls)] = string.Join(";", setupInfo.Addresses.Select(ip => IpAddressToUrl(ip, setupInfo.TcpPort, "tcp"))); if (setupInfo.EnableExperimentalFeatures) { settingsJson.Modifications[RavenConfiguration.GetKey(x => x.Core.FeaturesAvailability)] = FeaturesAvailability.Experimental; } var modifiedJsonObj = context.ReadObject(settingsJson, "modified-settings-json"); var indentedJson = SetupManager.IndentJsonString(modifiedJsonObj.ToString()); SetupManager.WriteSettingsJsonLocally(ServerStore.Configuration.ConfigPath, indentedJson); } return(NoContent()); }
public async Task DontKickFromClusterOnElectionTimeoutMismatch() { var cluster = await CreateRaftCluster(2, shouldRunInMemory : false); var result = await DisposeServerAndWaitForFinishOfDisposalAsync(cluster.Nodes[0]); await cluster.Nodes[1].ServerStore.WaitForState(RachisState.Candidate, CancellationToken.None); cluster.Nodes[0] = GetNewServer(new ServerCreationOptions { DeletePrevious = false, RunInMemory = false, DataDirectory = result.DataDirectory, CustomSettings = new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = result.Url, [RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout)] = 600.ToString(), } }); using (var cts = new CancellationTokenSource(10_000)) { var t1 = cluster.Nodes[0].ServerStore.WaitForState(RachisState.Leader, cts.Token); var t2 = cluster.Nodes[1].ServerStore.WaitForState(RachisState.Leader, cts.Token); var task = await Task.WhenAny(t1, t2); if (task == t1) { Assert.NotEqual(RachisState.Passive, cluster.Nodes[1].ServerStore.Engine.CurrentState); } else { Assert.NotEqual(RachisState.Passive, cluster.Nodes[0].ServerStore.Engine.CurrentState); } } result = await DisposeServerAndWaitForFinishOfDisposalAsync(cluster.Nodes[1]); cluster.Nodes[1] = GetNewServer(new ServerCreationOptions { DeletePrevious = false, RunInMemory = false, DataDirectory = result.DataDirectory, CustomSettings = new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = result.Url, [RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout)] = 600.ToString(), } }); using (var cts = new CancellationTokenSource(10_000)) { var t1 = cluster.Nodes[0].ServerStore.WaitForState(RachisState.Leader, cts.Token); var t2 = cluster.Nodes[1].ServerStore.WaitForState(RachisState.Leader, cts.Token); var task = await Task.WhenAny(t1, t2); if (task == t1) { Assert.Equal(RachisState.Follower, cluster.Nodes[1].ServerStore.Engine.CurrentState); } else { Assert.Equal(RachisState.Follower, cluster.Nodes[0].ServerStore.Engine.CurrentState); } } }
public async Task DontRemoveNodeWhileItHasNotReplicatedDocs() { var databaseName = GetDatabaseName(); var leader = await CreateRaftClusterAndGetLeader(3, shouldRunInMemory : false); using (var leaderStore = new DocumentStore { Urls = new[] { leader.WebUrl }, Database = databaseName, }) { leaderStore.Initialize(); var topology = new DatabaseTopology { Members = new List <string> { "B", "C" }, DynamicNodesDistribution = true }; var(index, dbGroupNodes) = await CreateDatabaseInCluster(new DatabaseRecord { DatabaseName = databaseName, Topology = topology }, 2, leader.WebUrl); await WaitForRaftIndexToBeAppliedInCluster(index, TimeSpan.FromSeconds(30)); using (var session = leaderStore.OpenSession()) { session.Store(new User(), "users/1"); session.SaveChanges(); } var dbToplogy = (await leaderStore.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(databaseName))).Topology; Assert.Equal(2, dbToplogy.AllNodes.Count()); Assert.Equal(0, dbToplogy.Promotables.Count); Assert.True(await WaitForDocumentInClusterAsync <User>(topology, databaseName, "users/1", null, TimeSpan.FromSeconds(30))); var serverA = Servers.Single(s => s.ServerStore.NodeTag == "A"); var urlsA = new[] { serverA.WebUrl }; var dataDirA = serverA.Configuration.Core.DataDirectory.FullPath.Split('/').Last(); DisposeServerAndWaitForFinishOfDisposal(serverA); var serverB = Servers.Single(s => s.ServerStore.NodeTag == "B"); var urlsB = new[] { serverB.WebUrl }; var dataDirB = serverB.Configuration.Core.DataDirectory.FullPath.Split('/').Last(); DisposeServerAndWaitForFinishOfDisposal(serverB); // write doc only to C using (var session = leaderStore.OpenSession()) { session.Store(new User(), "users/2"); session.SaveChanges(); } var serverC = Servers.Single(s => s.ServerStore.NodeTag == "C"); var urlsC = new[] { serverC.WebUrl }; var dataDirC = serverC.Configuration.Core.DataDirectory.FullPath.Split('/').Last(); DisposeServerAndWaitForFinishOfDisposal(serverC); Servers[0] = GetNewServer( new ServerCreationOptions { CustomSettings = new Dictionary <string, string> { { RavenConfiguration.GetKey(x => x.Core.ServerUrls), urlsA[0] } }, RunInMemory = false, DeletePrevious = false, PartialPath = dataDirA }); Servers[1] = GetNewServer(new ServerCreationOptions { CustomSettings = new Dictionary <string, string> { { RavenConfiguration.GetKey(x => x.Core.ServerUrls), urlsB[0] } }, RunInMemory = false, DeletePrevious = false, PartialPath = dataDirB }); await Task.Delay(TimeSpan.FromSeconds(10)); Assert.Equal(2, await WaitForValueAsync(async() => await GetMembersCount(leaderStore, databaseName), 2)); Assert.Equal(1, await WaitForValueAsync(async() => await GetRehabCount(leaderStore, databaseName), 1)); using (var session = leaderStore.OpenSession()) { session.Store(new User(), "users/3"); session.SaveChanges(); } Assert.True(await WaitForDocumentInClusterAsync <User>(new DatabaseTopology { Members = new List <string> { "A", "B" } }, databaseName, "users/3", null, TimeSpan.FromSeconds(10))); Servers[2] = GetNewServer(new ServerCreationOptions { CustomSettings = new Dictionary <string, string> { { RavenConfiguration.GetKey(x => x.Core.ServerUrls), urlsC[0] } }, RunInMemory = false, DeletePrevious = false, PartialPath = dataDirC }); Assert.Equal(2, await WaitForValueAsync(async() => await GetMembersCount(leaderStore, databaseName), 2)); Assert.Equal(0, await WaitForValueAsync(async() => await GetRehabCount(leaderStore, databaseName), 0, 30_000)); dbToplogy = (await leaderStore.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(databaseName))).Topology; Assert.True(await WaitForDocumentInClusterAsync <User>(dbToplogy, databaseName, "users/3", null, TimeSpan.FromSeconds(10))); dbToplogy = (await leaderStore.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(databaseName))).Topology; Assert.Equal(2, dbToplogy.AllNodes.Count()); Assert.Equal(2, dbToplogy.Members.Count); Assert.Equal(0, dbToplogy.Rehabs.Count); Assert.True(await WaitForDocumentInClusterAsync <User>(dbToplogy, databaseName, "users/1", null, TimeSpan.FromSeconds(10))); Assert.True(await WaitForDocumentInClusterAsync <User>(dbToplogy, databaseName, "users/3", null, TimeSpan.FromSeconds(10))); Assert.True(await WaitForDocumentInClusterAsync <User>(dbToplogy, databaseName, "users/2", null, TimeSpan.FromSeconds(30))); dbToplogy = (await leaderStore.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(databaseName))).Topology; Assert.Equal(2, dbToplogy.AllNodes.Count()); Assert.Equal(2, dbToplogy.Members.Count); Assert.Equal(0, dbToplogy.Rehabs.Count); } }
internal void ValidatePublicUrls() { if (PublicServerUrl.HasValue) { ValidatePublicUrl(PublicServerUrl.Value.UriValue, RavenConfiguration.GetKey(x => x.Core.PublicServerUrl)); } else if (ServerUrls.Length > 1) { throw new ArgumentException($"Configuration key '{RavenConfiguration.GetKey(x => x.Core.PublicServerUrl)}' must be specified when there is more than one '{RavenConfiguration.GetKey(x => x.Core.ServerUrls)}'."); } if (PublicTcpServerUrl.HasValue) { ValidatePublicUrl(PublicTcpServerUrl.Value.UriValue, RavenConfiguration.GetKey(x => x.Core.PublicTcpServerUrl)); } else if (TcpServerUrls != null && TcpServerUrls.Length > 1) { throw new ArgumentException($"Configuration key '{RavenConfiguration.GetKey(x => x.Core.PublicTcpServerUrl)}' must be specified when there is more than one '{RavenConfiguration.GetKey(x => x.Core.TcpServerUrls)}'."); } }
public async Task CanGetLetsEncryptCertificateAndRenewIt() { var settingPath = Path.Combine(NewDataPath(forceCreateDir: true), "settings.json"); var defaultSettingsPath = new PathSetting("settings.default.json").FullPath; File.Copy(defaultSettingsPath, settingPath, true); UseNewLocalServer(customConfigPath: settingPath); // Use this when testing against pebble //var acmeStaging = "https://localhost:14000/"; var acmeStaging = "https://acme-staging-v02.api.letsencrypt.org/"; Server.Configuration.Core.AcmeUrl = acmeStaging; Server.ServerStore.Configuration.Core.SetupMode = SetupMode.Initial; var domain = "RavenClusterTest" + Environment.MachineName.Replace("-", ""); string email; string rootDomain; Server.ServerStore.EnsureNotPassive(); var license = Server.ServerStore.LoadLicense(); using (var store = GetDocumentStore()) using (var commands = store.Commands()) using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context)) { var command = new ClaimDomainCommand(store.Conventions, context, new ClaimDomainInfo { Domain = domain, License = license }); await commands.RequestExecutor.ExecuteAsync(command, commands.Context); Assert.True(command.Result.RootDomains.Length > 0); rootDomain = command.Result.RootDomains[0]; email = command.Result.Email; } var tcpListener = new TcpListener(IPAddress.Loopback, 0); tcpListener.Start(); var port = ((IPEndPoint)tcpListener.LocalEndpoint).Port; tcpListener.Stop(); var setupInfo = new SetupInfo { Domain = domain, RootDomain = rootDomain, ModifyLocalServer = false, // N/A here RegisterClientCert = false, // N/A here Password = null, Certificate = null, LocalNodeTag = "A", License = license, Email = email, NodeSetupInfos = new Dictionary <string, SetupInfo.NodeInfo>() { ["A"] = new SetupInfo.NodeInfo { Port = port, Addresses = new List <string> { "127.0.0.1" } } } }; X509Certificate2 serverCert; byte[] serverCertBytes; string firstServerCertThumbprint; BlittableJsonReaderObject settingsJsonObject; using (var store = GetDocumentStore()) using (var commands = store.Commands()) using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context)) { var command = new SetupLetsEncryptCommand(store.Conventions, context, setupInfo); await commands.RequestExecutor.ExecuteAsync(command, commands.Context); Assert.True(command.Result.Length > 0); var zipBytes = command.Result; try { settingsJsonObject = SetupManager.ExtractCertificatesAndSettingsJsonFromZip(zipBytes, "A", context, out serverCertBytes, out serverCert, out _, out _, out _, out _); firstServerCertThumbprint = serverCert.Thumbprint; } catch (Exception e) { throw new InvalidOperationException("Unable to extract setup information from the zip file.", e); } // Finished the setup wizard, need to restart the server. // Since cannot restart we'll create a new server loaded with the new certificate and settings and use the server cert to connect to it settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Security.CertificatePassword), out string certPassword); settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Security.CertificateLetsEncryptEmail), out string letsEncryptEmail); settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.PublicServerUrl), out string publicServerUrl); settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.ServerUrls), out string serverUrl); settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.SetupMode), out SetupMode setupMode); settingsJsonObject.TryGet(RavenConfiguration.GetKey(x => x.Core.ExternalIp), out string externalIp); var tempFileName = GetTempFileName(); File.WriteAllBytes(tempFileName, serverCertBytes); IDictionary <string, string> customSettings = new ConcurrentDictionary <string, string> { [RavenConfiguration.GetKey(x => x.Security.CertificatePath)] = tempFileName, [RavenConfiguration.GetKey(x => x.Security.CertificateLetsEncryptEmail)] = letsEncryptEmail, [RavenConfiguration.GetKey(x => x.Security.CertificatePassword)] = certPassword, [RavenConfiguration.GetKey(x => x.Core.PublicServerUrl)] = publicServerUrl, [RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = serverUrl, [RavenConfiguration.GetKey(x => x.Core.SetupMode)] = setupMode.ToString(), [RavenConfiguration.GetKey(x => x.Core.ExternalIp)] = externalIp, [RavenConfiguration.GetKey(x => x.Core.AcmeUrl)] = acmeStaging }; DoNotReuseServer(customSettings); } Server.Dispose(); UseNewLocalServer(); // Note: because we use a staging lets encrypt cert, the chain is not trusted. // It only works because in the TestBase ctor we do: // RequestExecutor.ServerCertificateCustomValidationCallback += (msg, cert, chain, errors) => true; using (var store = GetDocumentStore(new Options { AdminCertificate = serverCert, ClientCertificate = serverCert })) using (var commands = store.Commands()) using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context)) { Server.ServerStore.EnsureNotPassive(); Assert.Equal(firstServerCertThumbprint, Server.Certificate.Certificate.Thumbprint); Server.Time.UtcDateTime = () => DateTime.UtcNow.AddDays(80); var mre = new ManualResetEventSlim(); Server.ServerCertificateChanged += (sender, args) => mre.Set(); var command = new ForceRenewCertCommand(store.Conventions, context); await commands.RequestExecutor.ExecuteAsync(command, commands.Context); Assert.True(command.Result.Success, "ForceRenewCertCommand returned false"); var result = mre.Wait(Debugger.IsAttached ? TimeSpan.FromMinutes(10) : TimeSpan.FromMinutes(4)); if (result == false && Server.RefreshTask.IsCompleted) { if (Server.RefreshTask.IsFaulted || Server.RefreshTask.IsCanceled) { Assert.True(result, $"Refresh task failed to complete successfully. Exception: {Server.RefreshTask.Exception}"); } Assert.True(result, "Refresh task completed successfully, waited too long for the cluster cert to be replaced"); } Assert.True(result, "Refresh task didn't complete. Waited too long for the cluster cert to be replaced"); Assert.NotEqual(firstServerCertThumbprint, Server.Certificate.Certificate.Thumbprint); } }
private static void BeforeSchemaUpgrade(StorageEnvironment storageEnvironment, ServerStore serverStore) { // doing this before the schema upgrade to allow to downgrade in case we cannot start the server using (var contextPool = new TransactionContextPool(storageEnvironment, serverStore.Configuration.Memory.MaxContextSizeToKeep)) { var license = serverStore.LoadLicense(contextPool); if (license == null) { return; } var licenseStatus = LicenseManager.GetLicenseStatus(license); if (licenseStatus.Expiration >= RavenVersionAttribute.Instance.ReleaseDate) { return; } string licenseJson = null; var fromPath = false; if (string.IsNullOrEmpty(serverStore.Configuration.Licensing.License) == false) { licenseJson = serverStore.Configuration.Licensing.License; } else if (File.Exists(serverStore.Configuration.Licensing.LicensePath.FullPath)) { try { licenseJson = File.ReadAllText(serverStore.Configuration.Licensing.LicensePath.FullPath); fromPath = true; } catch { // expected } } var errorMessage = $"Cannot start the RavenDB server because the expiration date of current license ({FormattedDateTime(licenseStatus.Expiration ?? DateTime.MinValue)}) " + $"is before the release date of this version ({FormattedDateTime(RavenVersionAttribute.Instance.ReleaseDate)})"; string expiredLicenseMessage = ""; if (string.IsNullOrEmpty(licenseJson) == false) { if (LicenseHelper.TryDeserializeLicense(licenseJson, out License localLicense)) { var localLicenseStatus = LicenseManager.GetLicenseStatus(localLicense); if (localLicenseStatus.Expiration >= RavenVersionAttribute.Instance.ReleaseDate) { serverStore.LicenseManager.OnBeforeInitialize += () => serverStore.LicenseManager.TryActivateLicense(throwOnActivationFailure: false); return; } var configurationKey = fromPath ? RavenConfiguration.GetKey(x => x.Licensing.LicensePath) : RavenConfiguration.GetKey(x => x.Licensing.License); expiredLicenseMessage = localLicense.Id == license.Id ? ". You can update current license using the setting.json file" : $". The license '{localLicense.Id}' obtained from '{configurationKey}' with expiration date of '{FormattedDateTime(localLicenseStatus.Expiration ?? DateTime.MinValue)}' is also expired."; } else { errorMessage += ". Could not parse the license from setting.json file."; throw new LicenseExpiredException(errorMessage); } } var licenseStorage = new LicenseStorage(); licenseStorage.Initialize(storageEnvironment, contextPool); var buildInfo = licenseStorage.GetBuildInfo(); if (buildInfo != null) { errorMessage += $" You can downgrade to the latest build that was working ({buildInfo.FullVersion})"; } if (string.IsNullOrEmpty(expiredLicenseMessage) == false) { errorMessage += expiredLicenseMessage; } throw new LicenseExpiredException(errorMessage);
public static int Main(string[] args) { NativeMemory.GetCurrentUnmanagedThreadId = () => (ulong)Pal.rvn_get_current_thread_id(); UseOnlyInvariantCultureInRavenDB(); SetCurrentDirectoryToServerPath(); string[] configurationArgs; try { configurationArgs = CommandLineSwitches.Process(args); } catch (CommandParsingException commandParsingException) { Console.WriteLine(commandParsingException.Message); CommandLineSwitches.ShowHelp(); return(1); } if (CommandLineSwitches.ShouldShowHelp) { CommandLineSwitches.ShowHelp(); return(0); } if (CommandLineSwitches.PrintVersionAndExit) { Console.WriteLine(ServerVersion.FullVersion); return(0); } new WelcomeMessage(Console.Out).Print(); var targetSettingsFile = new PathSetting(string.IsNullOrEmpty(CommandLineSwitches.CustomConfigPath) ? "settings.json" : CommandLineSwitches.CustomConfigPath); var destinationSettingsFile = new PathSetting("settings.default.json"); if (File.Exists(targetSettingsFile.FullPath) == false && File.Exists(destinationSettingsFile.FullPath)) //just in case { File.Copy(destinationSettingsFile.FullPath, targetSettingsFile.FullPath); } var configuration = RavenConfiguration.CreateForServer(null, CommandLineSwitches.CustomConfigPath); if (configurationArgs != null) { configuration.AddCommandLine(configurationArgs); } configuration.Initialize(); LoggingSource.UseUtcTime = configuration.Logs.UseUtcTime; LoggingSource.Instance.MaxFileSizeInBytes = configuration.Logs.MaxFileSize.GetValue(SizeUnit.Bytes); LoggingSource.Instance.SetupLogMode( configuration.Logs.Mode, configuration.Logs.Path.FullPath, configuration.Logs.RetentionTime?.AsTimeSpan, configuration.Logs.RetentionSize?.GetValue(SizeUnit.Bytes), configuration.Logs.Compress ); if (Logger.IsInfoEnabled) { Logger.Info($"Logging to {configuration.Logs.Path} set to {configuration.Logs.Mode} level."); } LatestVersionCheck.Instance.Initialize(configuration.Updates); if (Logger.IsOperationsEnabled) { Logger.Operations(RavenCli.GetInfoText()); } if (WindowsServiceRunner.ShouldRunAsWindowsService()) { try { WindowsServiceRunner.Run(CommandLineSwitches.ServiceName, configuration, configurationArgs); } catch (Exception e) { if (Logger.IsInfoEnabled) { Logger.Info("Error running Windows Service", e); } return(1); } return(0); } RestartServer = () => { RestartServerMre.Set(); ShutdownServerMre.Set(); }; var rerun = false; RavenConfiguration configBeforeRestart = configuration; do { if (rerun) { Console.WriteLine("\nRestarting Server..."); configuration = RavenConfiguration.CreateForServer(null, CommandLineSwitches.CustomConfigPath); if (configurationArgs != null) { var argsAfterRestart = PostSetupCliArgumentsUpdater.Process( configurationArgs, configBeforeRestart, configuration); configuration.AddCommandLine(argsAfterRestart); configBeforeRestart = configuration; } configuration.Initialize(); } try { using (var server = new RavenServer(configuration)) { try { try { server.OpenPipes(); } catch (Exception e) { if (Logger.IsInfoEnabled) { Logger.Info("Unable to OpenPipe. Admin Channel will not be available to the user", e); } Console.WriteLine("Warning: Admin Channel is not available:" + e); } server.BeforeSchemaUpgrade = x => BeforeSchemaUpgrade(x, server.ServerStore); server.Initialize(); if (CommandLineSwitches.PrintServerId) { Console.WriteLine($"Server ID is {server.ServerStore.GetServerId()}."); } new RuntimeSettings(Console.Out).Print(); if (rerun == false && CommandLineSwitches.LaunchBrowser) { BrowserHelper.OpenStudioInBrowser(server.ServerStore.GetNodeHttpServerUrl()); } new ClusterMessage(Console.Out, server.ServerStore).Print(); var prevColor = Console.ForegroundColor; Console.Write("Server available on: "); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"{server.ServerStore.GetNodeHttpServerUrl()}"); Console.ForegroundColor = prevColor; var tcpServerStatus = server.GetTcpServerStatus(); if (tcpServerStatus.Listeners.Count > 0) { prevColor = Console.ForegroundColor; Console.Write("Tcp listening on "); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"{string.Join(", ", tcpServerStatus.Listeners.Select(l => l.LocalEndpoint))}"); Console.ForegroundColor = prevColor; } Console.WriteLine("Server started, listening to requests..."); prevColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine("TIP: type 'help' to list the available commands."); Console.ForegroundColor = prevColor; if (configuration.Storage.IgnoreInvalidJournalErrors == true) { var message = $"Server is running in dangerous mode because {RavenConfiguration.GetKey(x => x.Storage.IgnoreInvalidJournalErrors)} was set. " + "It means that storages of databases, indexes and system one will be loaded regardless missing or corrupted journal files which " + "are mandatory to properly load the storage. " + "This switch is meant to be use only for recovery purposes. Please make sure that you won't use it on regular basis. "; if (Logger.IsOperationsEnabled) { Logger.Operations(message); } prevColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(message); Console.ForegroundColor = prevColor; } IsRunningNonInteractive = false; rerun = CommandLineSwitches.NonInteractive || configuration.Core.SetupMode == SetupMode.Initial ? RunAsNonInteractive() : RunInteractive(server); Console.WriteLine("Starting shut down..."); if (Logger.IsInfoEnabled) { Logger.Info("Server is shutting down"); } } catch (Exception e) { string message = null; if (e.InnerException is AddressInUseException) { message = $"{Environment.NewLine}Port might be already in use.{Environment.NewLine}Try running with an unused port.{Environment.NewLine}" + $"You can change the port using one of the following options:{Environment.NewLine}" + $"1) Change the ServerUrl property in setting.json file.{Environment.NewLine}" + $"2) Run the server from the command line with --ServerUrl option.{Environment.NewLine}" + $"3) Add RAVEN_ServerUrl to the Environment Variables.{Environment.NewLine}" + "For more information go to https://ravendb.net/l/EJS81M/5.0"; } else if (e is SocketException && PlatformDetails.RunningOnPosix) { const string extension = ".dll"; var ravenPath = typeof(RavenServer).Assembly.Location; if (ravenPath.EndsWith(extension, StringComparison.OrdinalIgnoreCase)) { ravenPath = ravenPath.Substring(0, ravenPath.Length - extension.Length); } message = $"{Environment.NewLine}In Linux low-level port (below 1024) will need a special permission, " + $"if this is your case please run{Environment.NewLine}" + $"sudo setcap CAP_NET_BIND_SERVICE=+eip {ravenPath}"; } else if (e.InnerException is LicenseExpiredException) { message = e.InnerException.Message; } if (Logger.IsOperationsEnabled) { Logger.Operations("Failed to initialize the server", e); Logger.Operations(message); } Console.WriteLine(message); Console.WriteLine(); Console.WriteLine(e); return(-1); } } Console.WriteLine("Shutdown completed"); } catch (Exception e) { Console.WriteLine("Error during shutdown"); Console.WriteLine(e); return(-2); } finally { if (Logger.IsOperationsEnabled) { Logger.OperationsAsync("Server has shut down").Wait(TimeSpan.FromSeconds(15)); } ShutdownCompleteMre.Set(); } } while (rerun); return(0); }
public async Task RestoreAndReplicateCounters() { var backupPath = NewDataPath(suffix: "BackupFolder"); using (var server = GetNewServer(new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Replication.MaxItemsCount)] = 1.ToString() })) { using (var store1 = GetDocumentStore(new Options { Server = server })) using (var store2 = GetDocumentStore(new Options { Server = server, CreateDatabase = false })) using (var store3 = GetDocumentStore(new Options { Server = server })) { using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Name1" }, "users/1"); await session.StoreAsync(new User { Name = "Name2" }, "users/2"); session.CountersFor("users/1").Increment("likes", 100); session.CountersFor("users/2").Increment("downloads", 500); await session.SaveChangesAsync(); } using (var session = store1.OpenAsyncSession()) { // need to be in a different transaction in order to split the replication into batches session.CountersFor("users/1").Increment("dislikes", 200); await session.SaveChangesAsync(); } await Backup(backupPath, store1); await Restore(backupPath, store2); var stats = await store2.Maintenance.SendAsync(new GetStatisticsOperation()); Assert.Equal(2, stats.CountOfDocuments); Assert.Equal(3, stats.CountOfCounters); using (var session = store2.OpenAsyncSession()) { await AssertCounters(session); } await SetupReplicationAsync(store2, store3); using (var session = store2.OpenAsyncSession()) { await session.StoreAsync(new User(), "marker"); await session.SaveChangesAsync(); } Assert.NotNull(WaitForDocumentToReplicate <User>(store3, "marker", 10_000)); using (var session = store3.OpenAsyncSession()) { await AssertCounters(session); } } } }
protected async Task <(List <RavenServer> Nodes, RavenServer Leader)> CreateRaftCluster(int numberOfNodes, bool shouldRunInMemory = true, int?leaderIndex = null, bool useSsl = false, IDictionary <string, string> customSettings = null) { leaderIndex = leaderIndex ?? _random.Next(0, numberOfNodes); RavenServer leader = null; var serversToPorts = new Dictionary <RavenServer, string>(); var clustersServers = new List <RavenServer>(); _electionTimeoutInMs = Math.Max(300, numberOfNodes * 80); for (var i = 0; i < numberOfNodes; i++) { customSettings = customSettings ?? new Dictionary <string, string>() { [RavenConfiguration.GetKey(x => x.Cluster.MoveToRehabGraceTime)] = "1", [RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout)] = _electionTimeoutInMs.ToString(), [RavenConfiguration.GetKey(x => x.Cluster.StabilizationTime)] = "1", }; string serverUrl; if (useSsl) { serverUrl = UseFiddlerUrl("https://127.0.0.1:0"); SetupServerAuthentication(customSettings, serverUrl); } else { serverUrl = UseFiddlerUrl("http://127.0.0.1:0"); customSettings[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = serverUrl; } var server = GetNewServer(customSettings, runInMemory: shouldRunInMemory); var port = Convert.ToInt32(server.ServerStore.GetNodeHttpServerUrl().Split(':')[2]); var prefix = useSsl ? "https" : "http"; serverUrl = UseFiddlerUrl($"{prefix}://127.0.0.1:{port}"); Servers.Add(server); clustersServers.Add(server); serversToPorts.Add(server, serverUrl); if (i == leaderIndex) { server.ServerStore.EnsureNotPassive(); leader = server; } } for (var i = 0; i < numberOfNodes; i++) { if (i == leaderIndex) { continue; } var follower = clustersServers[i]; // ReSharper disable once PossibleNullReferenceException await leader.ServerStore.AddNodeToClusterAsync(serversToPorts[follower]); await follower.ServerStore.WaitForTopology(Leader.TopologyModification.Voter); } // ReSharper disable once PossibleNullReferenceException var condition = await leader.ServerStore.WaitForState(RachisState.Leader, CancellationToken.None).WaitAsync(numberOfNodes * _electionTimeoutInMs * 5); var states = string.Empty; if (condition == false) { states = GetLastStatesFromAllServersOrderedByTime(); } Assert.True(condition, "The leader has changed while waiting for cluster to become stable. All nodes status: " + states); return(clustersServers, leader); }
public static void UnlikelyFailAuthorization(HttpContext context, string database, RavenServer.AuthenticateConnection feature, AuthorizationStatus authorizationStatus) { string message; if (feature == null || feature.Status == RavenServer.AuthenticationStatus.None || feature.Status == RavenServer.AuthenticationStatus.NoCertificateProvided) { message = "This server requires client certificate for authentication, but none was provided by the client."; } else { var name = feature.Certificate.FriendlyName; if (string.IsNullOrWhiteSpace(name)) { name = feature.Certificate.Subject; } if (string.IsNullOrWhiteSpace(name)) { name = feature.Certificate.ToString(false); } name += $"(Thumbprint: {feature.Certificate.Thumbprint})"; if (feature.Status == RavenServer.AuthenticationStatus.UnfamiliarCertificate) { message = $"The supplied client certificate '{name}' is unknown to the server. In order to register your certificate please contact your system administrator."; } else if (feature.Status == RavenServer.AuthenticationStatus.UnfamiliarIssuer) { message = $"The supplied client certificate '{name}' is unknown to the server but has a known Public Key Pinning Hash. Will not use it to authenticate because the issuer is unknown. To fix this, the admin can register the pinning hash of the *issuer* certificate: '{feature.IssuerHash}' in the '{RavenConfiguration.GetKey(x => x.Security.WellKnownIssuerHashes)}' configuration entry."; } else if (feature.Status == RavenServer.AuthenticationStatus.Allowed) { message = $"Could not authorize access to {(database ?? "the server")} using provided client certificate '{name}'."; } else if (feature.Status == RavenServer.AuthenticationStatus.Operator) { message = $"Insufficient security clearance to access {(database ?? "the server")} using provided client certificate '{name}'."; } else if (feature.Status == RavenServer.AuthenticationStatus.Expired) { message = $"The supplied client certificate '{name}' has expired on {feature.Certificate.NotAfter:D}. Please contact your system administrator in order to obtain a new one."; } else if (feature.Status == RavenServer.AuthenticationStatus.NotYetValid) { message = $"The supplied client certificate '{name}'cannot be used before {feature.Certificate.NotBefore:D}"; } else { message = "Access to the server was denied."; } } switch (authorizationStatus) { case AuthorizationStatus.ClusterAdmin: message += " ClusterAdmin access is required but not given to this certificate"; break; case AuthorizationStatus.Operator: message += " Operator/ClusterAdmin access is required but not given to this certificate"; break; case AuthorizationStatus.DatabaseAdmin: message += " DatabaseAdmin access is required but not given to this certificate"; break; } context.Response.StatusCode = (int)HttpStatusCode.Forbidden; using (var ctx = JsonOperationContext.ShortTermSingleUse()) using (var writer = new BlittableJsonTextWriter(ctx, context.Response.Body)) { DrainRequest(ctx, context); if (RavenServerStartup.IsHtmlAcceptable(context)) { context.Response.StatusCode = (int)HttpStatusCode.Redirect; context.Response.Headers["Location"] = "/auth-error.html?err=" + Uri.EscapeDataString(message); return; } ctx.Write(writer, new DynamicJsonValue { ["Type"] = "InvalidAuth", ["Message"] = message }); } }
public async Task MarkPolicyAfterRollup() { DefaultClusterSettings[RavenConfiguration.GetKey(x => x.Tombstones.CleanupInterval)] = 1.ToString(); var cluster = await CreateRaftCluster(3, watcherCluster : true); using (var store = GetDocumentStore(new Options { Server = cluster.Leader, ReplicationFactor = 3, RunInMemory = false })) { var raw = new RawTimeSeriesPolicy(); var config = new TimeSeriesConfiguration { Collections = new Dictionary <string, TimeSeriesCollectionConfiguration> { ["Users"] = new TimeSeriesCollectionConfiguration { RawPolicy = raw, Policies = new List <TimeSeriesPolicy> { new TimeSeriesPolicy("ByMinute", TimeSpan.FromMinutes(10)) } } }, PolicyCheckFrequency = TimeSpan.FromSeconds(1) }; DatabaseRecordWithEtag record = null; await WaitForValueAsync(async() => { record = await store.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(store.Database)); return(record.Topology.Members.Count); }, 3); Assert.Equal(3, record.Topology.Members.Count); var firstNode = record.Topology.Members[0]; await store.Maintenance.SendAsync(new ConfigureTimeSeriesOperation(config)); var now = new DateTime(2021, 6, 1, 10, 7, 29, DateTimeKind.Utc); foreach (var server in Servers) { var database = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); database.Time.UtcDateTime = () => now; } var baseline = now.Add(-TimeSpan.FromMinutes(15)); using (var session = store.OpenSession()) { var id = "users/karmel/0"; session.Store(new User { Name = "Karmel" }, id); for (int i = 0; i < 15; i++) { session.TimeSeriesFor(id, "Heartrate") .Append(baseline.AddMinutes(i), i); } session.SaveChanges(); } using (var session = store.OpenSession()) { session.Store(new User(), "marker"); session.SaveChanges(); Assert.True(await WaitForDocumentInClusterAsync <User>(cluster.Nodes, store.Database, "marker", null, TimeSpan.FromSeconds(15))); } var res = new Dictionary <string, int>(); foreach (var server in Servers) { var database = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); var tss = database.DocumentsStorage.TimeSeriesStorage; await database.TimeSeriesPolicyRunner.RunRollups(); var name = config.Collections["Users"].Policies[0].GetTimeSeriesName("Heartrate"); WaitForValue(() => { using (var session = store.OpenSession()) { var val = session.TimeSeriesFor("users/karmel/0", name) .Get(DateTime.MinValue, DateTime.MaxValue); return(val != null); } }, true); using (var session = store.OpenSession()) { var val = session.TimeSeriesFor("users/karmel/0", name) .Get(DateTime.MinValue, DateTime.MaxValue).Length; res.Add(server.ServerStore.NodeTag, val); Assert.True(val > 0); } } await WaitForValueAsync(async() => { record = await store.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(store.Database)); return(record.Topology.Members.Count); }, 3); Assert.Equal(3, record.Topology.Members.Count); var firstNode2 = record.Topology.Members[0]; Assert.Equal(firstNode2, firstNode); record = store.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(store.Database)).Result; var list = record.Topology.Members; list.Reverse(); await store.Maintenance.Server.SendAsync(new ReorderDatabaseMembersOperation(store.Database, list)); await WaitForValueAsync(async() => { record = await store.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(store.Database)); return(record.Topology.Members.Count); }, 3); Assert.Equal(3, record.Topology.Members.Count); firstNode2 = record.Topology.Members[0]; Assert.NotEqual(firstNode2, firstNode); await Task.Delay(1000); foreach (var server in Servers) { var database = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); database.Time.UtcDateTime = () => now.AddMinutes(10); await database.TimeSeriesPolicyRunner.RunRollups(); } foreach (var server in Servers) { WaitForValue(() => { using (var session = store.OpenSession()) { var name = config.Collections["Users"].Policies[0].GetTimeSeriesName("Heartrate"); var val = session.TimeSeriesFor("users/karmel/0", name) .Get(DateTime.MinValue, DateTime.MaxValue); return(val.Length > res[server.ServerStore.NodeTag]); } }, true); } using (var session = store.OpenSession()) { var name = config.Collections["Users"].Policies[0].GetTimeSeriesName("Heartrate"); var val = session.TimeSeriesFor("users/karmel/0", name) .Get(DateTime.MinValue, DateTime.MaxValue); Assert.True(val.Length > res[Servers[0].ServerStore.NodeTag]); } await WaitForValueAsync(async() => { record = await store.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(store.Database)); return(record.Topology.Members.Count); }, 3); Assert.Equal(3, record.Topology.Members.Count); firstNode2 = record.Topology.Members[0]; Assert.NotEqual(firstNode2, firstNode); } }
public static string GetError(string source, string dest) { return($"You are able to reach '{dest}', but the remote node failed to reach you back on '{source}'.{Environment.NewLine}" + $"Please validate the correctness of your '{RavenConfiguration.GetKey(x => x.Core.PublicServerUrl)}', '{RavenConfiguration.GetKey(x => x.Core.PublicTcpServerUrl)}' configuration and the firewall rules.{Environment.NewLine}" + $"Please visit https://ravendb.net/l/QUPWS7/4.1 for the RavenDB setup instructions."); }
public async Task SingleResultTest() { DefaultClusterSettings[RavenConfiguration.GetKey(x => x.Tombstones.CleanupInterval)] = 1.ToString(); var cluster = await CreateRaftCluster(3, watcherCluster : true); using (var store = GetDocumentStore(new Options { Server = cluster.Leader, ReplicationFactor = 3, RunInMemory = false })) { var raw = new RawTimeSeriesPolicy(); var config = new TimeSeriesConfiguration { Collections = new Dictionary <string, TimeSeriesCollectionConfiguration> { ["Users"] = new TimeSeriesCollectionConfiguration { RawPolicy = raw, Policies = new List <TimeSeriesPolicy> { new TimeSeriesPolicy("ByMinute", TimeSpan.FromSeconds(60)) } } }, PolicyCheckFrequency = TimeSpan.FromSeconds(1) }; await store.Maintenance.SendAsync(new ConfigureTimeSeriesOperation(config)); var now = DateTime.UtcNow; var baseline = now.AddSeconds(-120); using (var session = store.OpenSession()) { var id = "users/karmel/0"; session.Store(new User { Name = "Karmel" }, id); for (int i = 0; i < 120; i++) { session.TimeSeriesFor(id, "Heartrate") .Append(baseline.AddSeconds(i), i); } session.SaveChanges(); } using (var session = store.OpenSession()) { session.Store(new User(), "marker"); session.SaveChanges(); Assert.True(await WaitForDocumentInClusterAsync <User>(cluster.Nodes, store.Database, "marker", null, TimeSpan.FromSeconds(15))); } var database = await Servers[0].ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); using (database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext ctx)) { using (var tx = ctx.OpenReadTransaction()) { var name = config.Collections["Users"].Policies[0].GetTimeSeriesName("Heartrate"); var reader = database.DocumentsStorage.TimeSeriesStorage.GetReader(ctx, "users/karmel/0", "Heartrate", baseline.AddSeconds(120), DateTime.MaxValue); Assert.True(reader.Last() == null); } } } }
internal static void Validate(RavenConfiguration configuration) { foreach (var sUrl in configuration.Core.ServerUrls) { var serverUrl = sUrl.ToLowerInvariant(); if (Uri.TryCreate(serverUrl, UriKind.Absolute, out var uri) == false) { throw new UriFormatException("Unable to parse URL - " + serverUrl); } var isServerUrlHttps = uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase); var serverAddresses = DetermineServerIp(uri); var unsecuredAccessAddressRange = configuration.Security.UnsecuredAccessAllowed; var serverIsWithinUnsecuredAccessRange = serverAddresses.Any(x => SecurityUtils.IsUnsecuredAccessAllowedForAddress(unsecuredAccessAddressRange, x)); if (configuration.Security.AuthenticationEnabled) { if (isServerUrlHttps == false) { throw new InvalidOperationException( $"When the server certificate in either `{RavenConfiguration.GetKey(x => x.Security.CertificatePath)}` or `{RavenConfiguration.GetKey(x => x.Security.CertificateExec)}` is specified, the `{RavenConfiguration.GetKey(x => x.Core.ServerUrls)}` must be using https, but was " + serverUrl); } } else { if (isServerUrlHttps) { throw new InvalidOperationException($"Configured server address { string.Join(", ", configuration.Core.ServerUrls) } requires HTTPS. Please set up certification information under { RavenConfiguration.GetKey(x => x.Security.CertificatePath) } configuration key."); } if (serverIsWithinUnsecuredAccessRange == false) { configuration.Security.UnsecureAccessWarningMessage = $"Configured {RavenConfiguration.GetKey(x => x.Core.ServerUrls)} \"{string.Join(", ", configuration.Core.ServerUrls)}\" is not set within allowed unsecured access address range - { configuration.Security.UnsecuredAccessAllowed }. Use a server url within unsecure access address range ({RavenConfiguration.GetKey(x => x.Security.UnsecuredAccessAllowed)} option) or fill in server certificate information."; configuration.Security.IsUnsecureAccessSetupValid = false; } } if (configuration.Security.IsUnsecureAccessSetupValid.HasValue == false) { configuration.Security.IsUnsecureAccessSetupValid = true; } } }
public async Task MoveToPassiveWhenRefusedConnectionFromAllNodes() { //DebuggerAttachedTimeout.DisableLongTimespan = true; var clusterSize = 3; var databaseName = GetDatabaseName(); var leader = await CreateRaftClusterAndGetLeader(clusterSize, false, 0, customSettings : new Dictionary <string, string>() { [RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout)] = "600" }); using (var store = new DocumentStore { Urls = new[] { leader.WebUrl }, Database = databaseName }.Initialize()) { var doc = new DatabaseRecord(databaseName); var databaseResult = await store.Maintenance.Server.SendAsync(new CreateDatabaseOperation(doc, clusterSize)); Assert.Equal(clusterSize, databaseResult.Topology.Members.Count); await WaitForRaftIndexToBeAppliedInCluster(databaseResult.RaftCommandIndex, TimeSpan.FromSeconds(10)); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User()); await session.SaveChangesAsync(); } var dataDir = Servers[1].Configuration.Core.DataDirectory.FullPath.Split('/').Last(); var urls = new[] { Servers[1].WebUrl }; var nodeTag = Servers[1].ServerStore.NodeTag; // kill the process and remove the node from topology DisposeServerAndWaitForFinishOfDisposal(Servers[1]); await ActionWithLeader((l) => l.ServerStore.RemoveFromClusterAsync(nodeTag)); using (leader.ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context)) { var val = await WaitForValueAsync(() => { using (context.OpenReadTransaction()) { return(Servers[2].ServerStore.GetClusterTopology(context).AllNodes.Count); } }, clusterSize - 1); Assert.Equal(clusterSize - 1, val); val = await WaitForValueAsync(() => { using (context.OpenReadTransaction()) { return(Servers[0].ServerStore.GetClusterTopology(context).AllNodes.Count); } }, clusterSize - 1); Assert.Equal(clusterSize - 1, val); } // bring the node back to live and ensure that he moves to passive state Servers[1] = GetNewServer( new ServerCreationOptions { CustomSettings = new Dictionary <string, string> { { RavenConfiguration.GetKey(x => x.Core.PublicServerUrl), urls[0] }, { RavenConfiguration.GetKey(x => x.Core.ServerUrls), urls[0] }, { RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout), "600" } }, RunInMemory = false, DeletePrevious = false, PartialPath = dataDir }); Assert.True(await Servers[1].ServerStore.WaitForState(RachisState.Passive, CancellationToken.None).WaitAsync(TimeSpan.FromSeconds(30)), "1st assert"); // rejoin the node to the cluster await ActionWithLeader((l) => l.ServerStore.AddNodeToClusterAsync(urls[0], nodeTag)); Assert.True(await Servers[1].ServerStore.WaitForState(RachisState.Follower, CancellationToken.None).WaitAsync(TimeSpan.FromSeconds(30)), "2nd assert"); } }
public async Task ShouldWork() { var backupPath = NewDataPath(suffix: "BackupFolder"); using (var store = GetDocumentStore(new Options { ModifyDatabaseRecord = record => record.Settings[RavenConfiguration.GetKey(x => x.PerformanceHints.MaxNumberOfResults)] = "1" })) { store.Maintenance.Send(new CreateSampleDataOperation()); WaitForIndexing(store); using (var session = store.OpenSession()) { session.Query <Employee>().ToList(); // this will generate performance hint } var database = await GetDatabase(store.Database); database.NotificationCenter.Paging.UpdatePaging(null); int beforeBackupAlertCount; using (database.NotificationCenter.GetStored(out var actions)) beforeBackupAlertCount = actions.Count(); Assert.True(beforeBackupAlertCount > 0); var beforeBackupStats = store.Maintenance.Send(new GetStatisticsOperation()); var config = new PeriodicBackupConfiguration { BackupType = BackupType.Snapshot, LocalSettings = new LocalSettings { FolderPath = backupPath }, IncrementalBackupFrequency = "* * * * *" //every minute }; var backupTaskId = (store.Maintenance.Send(new UpdatePeriodicBackupOperation(config))).TaskId; store.Maintenance.Send(new StartBackupOperation(true, backupTaskId)); var operation = new GetPeriodicBackupStatusOperation(backupTaskId); SpinWait.SpinUntil(() => { var getPeriodicBackupResult = store.Maintenance.Send(operation); return(getPeriodicBackupResult.Status?.LastEtag > 0); }, TimeSpan.FromSeconds(15)); // restore the database with a different name var restoredDatabaseName = $"restored_database_snapshot_{Guid.NewGuid().ToString()}"; var restoreConfiguration = new RestoreBackupConfiguration { BackupLocation = Directory.GetDirectories(backupPath).First(), DatabaseName = restoredDatabaseName }; var restoreBackupTask = new RestoreBackupOperation(restoreConfiguration); var restoreResult = store.Maintenance.Server.Send(restoreBackupTask); restoreResult.WaitForCompletion(TimeSpan.FromSeconds(30)); var afterRestoreStats = store.Maintenance.ForDatabase(restoredDatabaseName).Send(new GetStatisticsOperation()); var restoredDatabase = await GetDatabase(restoredDatabaseName); int afterRestoreAlertCount; using (restoredDatabase.NotificationCenter.GetStored(out var actions)) afterRestoreAlertCount = actions.Count(); Assert.True(afterRestoreAlertCount > 0); var indexesPath = restoredDatabase.Configuration.Indexing.StoragePath; var indexesDirectory = new DirectoryInfo(indexesPath.FullPath); Assert.True(indexesDirectory.Exists); Assert.Equal(afterRestoreStats.CountOfIndexes, indexesDirectory.GetDirectories().Length); Assert.NotEqual(beforeBackupStats.DatabaseId, afterRestoreStats.DatabaseId); Assert.Equal(beforeBackupStats.CountOfAttachments, afterRestoreStats.CountOfAttachments); Assert.Equal(beforeBackupStats.CountOfConflicts, afterRestoreStats.CountOfConflicts); Assert.Equal(beforeBackupStats.CountOfDocuments, afterRestoreStats.CountOfDocuments); Assert.Equal(beforeBackupStats.CountOfDocumentsConflicts, afterRestoreStats.CountOfDocumentsConflicts); Assert.Equal(beforeBackupStats.CountOfIndexes, afterRestoreStats.CountOfIndexes); Assert.Equal(beforeBackupStats.CountOfRevisionDocuments, afterRestoreStats.CountOfRevisionDocuments); Assert.Equal(beforeBackupStats.CountOfTombstones, afterRestoreStats.CountOfTombstones); Assert.Equal(beforeBackupStats.CountOfUniqueAttachments, afterRestoreStats.CountOfUniqueAttachments); } }
public async Task ChangeUrlOfMultiNodeCluster() { var fromSeconds = TimeSpan.FromSeconds(8); var databaseName = GetDatabaseName(); var groupSize = 3; var newUrl = "http://127.0.0.1:0"; string nodeTag; var leader = await CreateRaftClusterAndGetLeader(groupSize, shouldRunInMemory : false, leaderIndex : 0, customSettings : new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Cluster.MoveToRehabGraceTime)] = "4" }); using (var leaderStore = new DocumentStore { Urls = new[] { leader.WebUrl }, Database = databaseName }) { leaderStore.Initialize(); await CreateDatabaseInCluster(databaseName, groupSize, leader.WebUrl); var dbToplogy = (await leaderStore.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(databaseName))).Topology; Assert.Equal(groupSize, dbToplogy.Members.Count); var dataDir = Servers[1].Configuration.Core.DataDirectory.FullPath.Split('/').Last(); nodeTag = Servers[1].ServerStore.NodeTag; // kill and change the url DisposeServerAndWaitForFinishOfDisposal(Servers[1]); var customSettings = new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = newUrl, [RavenConfiguration.GetKey(x => x.Security.UnsecuredAccessAllowed)] = UnsecuredAccessAddressRange.PublicNetwork.ToString() }; Servers[1] = GetNewServer(new ServerCreationOptions { CustomSettings = customSettings, RunInMemory = false, DeletePrevious = false, PartialPath = dataDir }); newUrl = Servers[1].WebUrl; // ensure that at this point we still can't talk to node await Task.Delay(fromSeconds); // wait for the observer to update the status dbToplogy = (await leaderStore.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(databaseName))).Topology; Assert.Equal(1, dbToplogy.Rehabs.Count); Assert.Equal(groupSize - 1, dbToplogy.Members.Count); } await WaitForLeader(fromSeconds); leader = Servers.Single(s => s.Disposed == false && s.ServerStore.IsLeader()); // remove and rejoin to change the url Assert.True(await leader.ServerStore.RemoveFromClusterAsync(nodeTag).WaitAsync(fromSeconds)); Assert.True(await Servers[1].ServerStore.WaitForState(RachisState.Passive, CancellationToken.None).WaitAsync(fromSeconds)); Assert.True(await leader.ServerStore.AddNodeToClusterAsync(Servers[1].ServerStore.GetNodeHttpServerUrl(), nodeTag).WaitAsync(fromSeconds)); Assert.True(await Servers[1].ServerStore.WaitForState(RachisState.Follower, CancellationToken.None).WaitAsync(fromSeconds)); Assert.Equal(3, WaitForValue(() => leader.ServerStore.GetClusterTopology().Members.Count, 3)); // create a new database and verify that it resides on the server with the new url var(_, dbGroupNodes) = await CreateDatabaseInCluster(GetDatabaseName(), groupSize, leader.WebUrl); Assert.True(dbGroupNodes.Select(s => s.WebUrl).Contains(newUrl)); }
public async Task TwoWayExternalReplicationShouldNotLoadIdleDatabase() { using (var server = GetNewServer(new ServerCreationOptions { CustomSettings = new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Databases.MaxIdleTime)] = "10", [RavenConfiguration.GetKey(x => x.Databases.FrequencyToCheckForIdle)] = "3", [RavenConfiguration.GetKey(x => x.Core.RunInMemory)] = "false" } })) using (var store1 = GetDocumentStore(new Options { Server = server, RunInMemory = false })) using (var store2 = GetDocumentStore(new Options { Server = server, RunInMemory = false })) { var externalTask1 = new ExternalReplication(store2.Database, "MyConnectionString1") { Name = "MyExternalReplication1" }; var externalTask2 = new ExternalReplication(store1.Database, "MyConnectionString2") { Name = "MyExternalReplication2" }; await AddWatcherToReplicationTopology(store1, externalTask1); await AddWatcherToReplicationTopology(store2, externalTask2); Assert.True(server.ServerStore.DatabasesLandlord.LastRecentlyUsed.TryGetValue(store1.Database, out _)); Assert.True(server.ServerStore.DatabasesLandlord.LastRecentlyUsed.TryGetValue(store2.Database, out _)); var now = DateTime.Now; var nextNow = now + TimeSpan.FromSeconds(60); while (now < nextNow && server.ServerStore.IdleDatabases.Count < 2) { Thread.Sleep(3000); now = DateTime.Now; } Assert.Equal(2, server.ServerStore.IdleDatabases.Count); await store1.Maintenance.SendAsync(new CreateSampleDataOperation()); WaitForIndexing(store1); var count = 0; var docs = store1.Maintenance.Send(new GetStatisticsOperation()).CountOfDocuments; var replicatedDocs = store2.Maintenance.Send(new GetStatisticsOperation()).CountOfDocuments; while (docs != replicatedDocs && count < 20) { Thread.Sleep(3000); replicatedDocs = store2.Maintenance.Send(new GetStatisticsOperation()).CountOfDocuments; count++; } Assert.Equal(docs, replicatedDocs); count = 0; nextNow = DateTime.Now + TimeSpan.FromMinutes(5); while (server.ServerStore.IdleDatabases.Count == 0 && now < nextNow) { Thread.Sleep(500); if (count % 10 == 0) { store1.Maintenance.Send(new GetStatisticsOperation()); } now = DateTime.Now; count++; } Assert.Equal(1, server.ServerStore.IdleDatabases.Count); nextNow = DateTime.Now + TimeSpan.FromSeconds(15); while (now < nextNow) { Thread.Sleep(2000); store1.Maintenance.Send(new GetStatisticsOperation()); Assert.Equal(1, server.ServerStore.IdleDatabases.Count); now = DateTime.Now; } nextNow = DateTime.Now + TimeSpan.FromMinutes(10); while (now < nextNow && server.ServerStore.IdleDatabases.Count < 2) { Thread.Sleep(3000); now = DateTime.Now; } Assert.Equal(2, server.ServerStore.IdleDatabases.Count); using (var s = store2.OpenSession()) { s.Advanced.RawQuery <dynamic>("from @all_docs") .ToList(); } Assert.Equal(1, server.ServerStore.IdleDatabases.Count); var operation = await store2 .Operations .ForDatabase(store2.Database) .SendAsync(new PatchByQueryOperation("from Companies update { this.Name = this.Name + '_patched'; }")); await operation.WaitForCompletionAsync(); nextNow = DateTime.Now + TimeSpan.FromMinutes(2); while (now < nextNow && server.ServerStore.IdleDatabases.Count > 0) { Thread.Sleep(5000); now = DateTime.Now; } Assert.Equal(0, server.ServerStore.IdleDatabases.Count); nextNow = DateTime.Now + TimeSpan.FromMinutes(10); while (server.ServerStore.IdleDatabases.Count == 0 && now < nextNow) { Thread.Sleep(500); if (count % 10 == 0) { store2.Maintenance.Send(new GetStatisticsOperation()); } now = DateTime.Now; count++; } Assert.Equal(1, server.ServerStore.IdleDatabases.Count); nextNow = DateTime.Now + TimeSpan.FromSeconds(15); while (now < nextNow) { Thread.Sleep(2000); store2.Maintenance.Send(new GetStatisticsOperation()); Assert.Equal(1, server.ServerStore.IdleDatabases.Count); now = DateTime.Now; } } }
private void WriteDatabaseInfo(string databaseName, BlittableJsonReaderObject dbRecordBlittable, TransactionOperationContext context, AbstractBlittableJsonTextWriter writer) { try { var online = ServerStore.DatabasesLandlord.DatabasesCache.TryGetValue(databaseName, out Task <DocumentDatabase> dbTask) && dbTask != null && dbTask.IsCompleted; // Check for exceptions if (dbTask != null && dbTask.IsFaulted) { var exception = dbTask.Exception.ExtractSingleInnerException(); WriteFaultedDatabaseInfo(databaseName, exception, context, writer); return; } var dbRecord = JsonDeserializationCluster.DatabaseRecord(dbRecordBlittable); var db = online ? dbTask.Result : null; var indexingStatus = db?.IndexStore?.Status ?? IndexRunningStatus.Running; // Looking for disabled indexing flag inside the database settings for offline database status if (dbRecord.Settings.TryGetValue(RavenConfiguration.GetKey(x => x.Indexing.Disabled), out var val) && bool.TryParse(val, out var indexingDisabled) && indexingDisabled) { indexingStatus = IndexRunningStatus.Disabled; } var disabled = dbRecord.Disabled; var topology = dbRecord.Topology; var clusterTopology = ServerStore.GetClusterTopology(context); clusterTopology.ReplaceCurrentNodeUrlWithClientRequestedNodeUrlIfNecessary(ServerStore, HttpContext); var nodesTopology = new NodesTopology(); var statuses = ServerStore.GetNodesStatuses(); if (topology != null) { foreach (var member in topology.Members) { var url = clusterTopology.GetUrlFromTag(member); var node = new InternalReplication { Database = databaseName, NodeTag = member, Url = url }; nodesTopology.Members.Add(GetNodeId(node)); SetNodeStatus(topology, member, nodesTopology, statuses); } foreach (var promotable in topology.Promotables) { topology.PredefinedMentors.TryGetValue(promotable, out var mentorCandidate); var node = GetNode(databaseName, clusterTopology, promotable, mentorCandidate, out var promotableTask); var mentor = topology.WhoseTaskIsIt(ServerStore.Engine.CurrentState, promotableTask, null); nodesTopology.Promotables.Add(GetNodeId(node, mentor)); SetNodeStatus(topology, promotable, nodesTopology, statuses); } foreach (var rehab in topology.Rehabs) { var node = GetNode(databaseName, clusterTopology, rehab, null, out var promotableTask); var mentor = topology.WhoseTaskIsIt(ServerStore.Engine.CurrentState, promotableTask, null); nodesTopology.Rehabs.Add(GetNodeId(node, mentor)); SetNodeStatus(topology, rehab, nodesTopology, statuses); } } if (online == false) { // if state of database is found in the cache we can continue if (ServerStore.DatabaseInfoCache.TryGet(databaseName, databaseInfoJson => { databaseInfoJson.Modifications = new DynamicJsonValue(databaseInfoJson) { [nameof(DatabaseInfo.Disabled)] = disabled, [nameof(DatabaseInfo.IndexingStatus)] = indexingStatus.ToString(), [nameof(DatabaseInfo.NodesTopology)] = nodesTopology.ToJson(), [nameof(DatabaseInfo.DeletionInProgress)] = DynamicJsonValue.Convert(dbRecord.DeletionInProgress) }; context.Write(writer, databaseInfoJson); })) { return; } // we won't find it if it is a new database or after a dirty shutdown, // so just report empty values then } var size = db?.GetSizeOnDisk() ?? (new Size(0), new Size(0)); var databaseInfo = new DatabaseInfo { Name = databaseName, Disabled = disabled, TotalSize = size.Data, TempBuffersSize = size.TempBuffers, IsAdmin = true, IsEncrypted = dbRecord.Encrypted, UpTime = online ? (TimeSpan?)GetUptime(db) : null, BackupInfo = GetBackupInfo(db), Alerts = db?.NotificationCenter.GetAlertCount() ?? 0, RejectClients = false, LoadError = null, IndexingErrors = db?.IndexStore?.GetIndexes()?.Sum(index => index.GetErrorCount()) ?? 0, DocumentsCount = db?.DocumentsStorage.GetNumberOfDocuments() ?? 0, HasRevisionsConfiguration = db?.DocumentsStorage.RevisionsStorage.Configuration != null, HasExpirationConfiguration = db?.ExpiredDocumentsCleaner != null, IndexesCount = db?.IndexStore?.GetIndexes()?.Count() ?? 0, IndexingStatus = indexingStatus, NodesTopology = nodesTopology, ReplicationFactor = topology?.ReplicationFactor ?? -1, DynamicNodesDistribution = topology?.DynamicNodesDistribution ?? false, DeletionInProgress = dbRecord.DeletionInProgress }; var doc = databaseInfo.ToJson(); context.Write(writer, doc); } catch (Exception e) { if (Logger.IsInfoEnabled) { Logger.Info($"Failed to get database info for: {databaseName}", e); } WriteFaultedDatabaseInfo(databaseName, e, context, writer); } }
public void CertificateAndMasterKeyExecTest() { string script; IDictionary <string, string> customSettings = new ConcurrentDictionary <string, string>(); var keyPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var buffer = new byte[256 / 8]; using (var cryptoRandom = new RNGCryptoServiceProvider()) { cryptoRandom.GetBytes(buffer); } File.WriteAllBytes(keyPath, buffer); var certPath = GenerateAndSaveSelfSignedCertificate(); if (PlatformDetails.RunningOnPosix) { var scriptPath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".sh")); var keyArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { scriptPath, keyPath }); var certArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { scriptPath, certPath.ServerCertificatePath }); customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExec)] = "bash"; customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExecArguments)] = $"{keyArgs}"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "bash"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{certArgs}"; customSettings[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; script = "#!/bin/bash\ncat \"$1\""; File.WriteAllText(scriptPath, script); Process.Start("chmod", $"700 {scriptPath}"); } else { var scriptPath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1")); var keyArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", scriptPath, keyPath }); var certArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> { "-NoProfile", scriptPath, certPath.ServerCertificatePath }); customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExec)] = "powershell"; customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExecArguments)] = $"{keyArgs}"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)] = "powershell"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExec)] = "powershell"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExec)] = "powershell"; customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{certArgs}"; customSettings[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0"; script = @"param([string]$userArg) try { $bytes = Get-Content -path $userArg -encoding Byte $stdout = [System.Console]::OpenStandardOutput() $stdout.Write($bytes, 0, $bytes.Length) } catch { Write-Error $_.Exception exit 1 } exit 0"; File.WriteAllText(scriptPath, script); } UseNewLocalServer(customSettings: customSettings, runInMemory: false); // The master key loading is lazy, let's put a database secret key to invoke it. var dbName = GetDatabaseName(); var databaseKey = new byte[32]; using (var rand = RandomNumberGenerator.Create()) { rand.GetBytes(databaseKey); } var base64Key = Convert.ToBase64String(databaseKey); // sometimes when using `dotnet xunit` we get platform not supported from ProtectedData try { ProtectedData.Protect(Encoding.UTF8.GetBytes("Is supported?"), null, DataProtectionScope.CurrentUser); } catch (PlatformNotSupportedException) { return; } Server.ServerStore.PutSecretKey(base64Key, dbName, true); X509Certificate2 serverCertificate; try { serverCertificate = new X509Certificate2(certPath.ServerCertificatePath, (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); } catch (CryptographicException e) { throw new CryptographicException($"Failed to load the test certificate from {certPath}.", e); } using (var store = GetDocumentStore(new Options { AdminCertificate = serverCertificate, ClientCertificate = serverCertificate, ModifyDatabaseName = s => dbName, ModifyDatabaseRecord = record => record.Encrypted = true, Path = NewDataPath() })) { } var secrets = Server.ServerStore.Secrets; var serverMasterKey = (Lazy <byte[]>) typeof(SecretProtection).GetField("_serverMasterKey", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(secrets); Assert.True(serverMasterKey.Value.SequenceEqual(buffer)); Assert.True(Server.Certificate.Certificate.Equals(serverCertificate)); }
public async Task ClusterTransactionRequestWithRevisions() { var leader = await CreateRaftClusterAndGetLeader(5, shouldRunInMemory : false, leaderIndex : 0); using (var leaderStore = GetDocumentStore(new Options { DeleteDatabaseOnDispose = false, Server = leader, ReplicationFactor = 5, ModifyDocumentStore = (store) => store.Conventions.DisableTopologyUpdates = true })) { var user1 = new User() { Name = "Karmel" }; var user3 = new User() { Name = "Indych" }; var index = await RevisionsHelper.SetupRevisions(leader.ServerStore, leaderStore.Database, configuration => configuration.Collections["Users"].PurgeOnDelete = false); await WaitForRaftIndexToBeAppliedInCluster(index, TimeSpan.FromSeconds(15)); // bring our SUT node down, but we still have a cluster and can execute cluster transaction. var server = Servers[1]; var url = server.WebUrl; var dataDir = Servers[1].Configuration.Core.DataDirectory.FullPath.Split('/').Last(); await DisposeServerAndWaitForFinishOfDisposalAsync(server); using (var session = leaderStore.OpenAsyncSession(new SessionOptions { TransactionMode = TransactionMode.ClusterWide })) { Assert.Equal(1, session.Advanced.RequestExecutor.TopologyNodes.Count); Assert.Equal(leader.WebUrl, session.Advanced.RequestExecutor.Url); session.Advanced.ClusterTransaction.CreateCompareExchangeValue("usernames/ayende", user1); await session.StoreAsync(user3, "foo/bar"); await session.SaveChangesAsync(); var user = (await session.Advanced.ClusterTransaction.GetCompareExchangeValueAsync <User>("usernames/ayende")).Value; Assert.Equal(user1.Name, user.Name); user = await session.LoadAsync <User>("foo/bar"); Assert.Equal(user3.Name, user.Name); var list = await session.Advanced.Revisions.GetForAsync <User>(user.Id); Assert.Equal(1, list.Count); var changeVector = session.Advanced.GetChangeVectorFor(user); Assert.NotNull(await session.Advanced.Revisions.GetAsync <User>(changeVector)); } // bring more nodes down, so only one node is left var dataDir2 = Servers[2].Configuration.Core.DataDirectory.FullPath.Split('/').Last(); var url2 = Servers[2].WebUrl; var task1 = DisposeServerAndWaitForFinishOfDisposalAsync(Servers[2]); var task2 = DisposeServerAndWaitForFinishOfDisposalAsync(Servers[3]); var task3 = DisposeServerAndWaitForFinishOfDisposalAsync(Servers[4]); await Task.WhenAll(task1, task2, task3); using (var session = leaderStore.OpenAsyncSession()) { Assert.Equal(leader.WebUrl, session.Advanced.RequestExecutor.Url); await session.StoreAsync(user1, "foo/bar"); await session.SaveChangesAsync(); var list = await session.Advanced.Revisions.GetForAsync <User>(user1.Id); Assert.Equal(2, list.Count); } long lastRaftIndex; using (leader.ServerStore.Engine.ContextPool.AllocateOperationContext(out TransactionOperationContext ctx)) using (ctx.OpenReadTransaction()) { lastRaftIndex = leader.ServerStore.Engine.GetLastCommitIndex(ctx); } // revive the SUT node var revived = Servers[1] = GetNewServer(new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = url, [RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout)] = "400" }, runInMemory: false, deletePrevious: false, partialPath: dataDir); using (var revivedStore = new DocumentStore() { Urls = new[] { revived.WebUrl }, Database = leaderStore.Database, Conventions = new DocumentConventions { DisableTopologyUpdates = true } }.Initialize()) { // let the document with the revision to replicate Assert.True(WaitForDocument(revivedStore, "foo/bar")); using (var session = revivedStore.OpenAsyncSession()) { var user = await session.LoadAsync <User>("foo/bar"); var changeVector = session.Advanced.GetChangeVectorFor(user); Assert.NotNull(await session.Advanced.Revisions.GetAsync <User>(changeVector)); var list = await session.Advanced.Revisions.GetForAsync <User>("foo/bar"); Assert.Equal(2, list.Count); // revive another node so we should have a functional cluster now Servers[2] = GetNewServer(new Dictionary <string, string> { [RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = url2, [RavenConfiguration.GetKey(x => x.Cluster.ElectionTimeout)] = "400" }, runInMemory: false, deletePrevious: false, partialPath: dataDir2); // wait for the log to apply on the SUT node using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15))) { await leader.ServerStore.Engine.WaitForLeaveState(RachisState.Candidate, cts.Token); } var database = await Servers[1].ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(leaderStore.Database); await database.RachisLogIndexNotifications.WaitForIndexNotification(lastRaftIndex, TimeSpan.FromSeconds(15)); list = await session.Advanced.Revisions.GetForAsync <User>("foo/bar"); Assert.Equal(2, list.Count); } } } }
public async Task OnDirectoryInitializeInMemoryTest() { string script; IDictionary <string, string> customSettings = new ConcurrentDictionary <string, string>(); var scriptFile = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1")); var outputFile = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".txt")); if (PlatformDetails.RunningOnPosix) { customSettings[RavenConfiguration.GetKey(x => x.Storage.OnDirectoryInitializeExec)] = "bash"; customSettings[RavenConfiguration.GetKey(x => x.Storage.OnDirectoryInitializeExecArguments)] = $"{scriptFile} {outputFile}"; script = "#!/bin/bash\r\necho \"$2 $3 $4 $5 $6\" >> $1"; File.WriteAllText(scriptFile, script); Process.Start("chmod", $"700 {scriptFile}"); } else { customSettings[RavenConfiguration.GetKey(x => x.Storage.OnDirectoryInitializeExec)] = "powershell"; customSettings[RavenConfiguration.GetKey(x => x.Storage.OnDirectoryInitializeExecArguments)] = $"{scriptFile} {outputFile}"; script = @" param([string]$userArg ,[string]$type, [string]$name, [string]$dataPath, [string]$tempPath, [string]$journalPath) Add-Content $userArg ""$type $name $dataPath $tempPath $journalPath\r\n"" exit 0"; File.WriteAllText(scriptFile, script); } UseNewLocalServer(customSettings: customSettings); // Creating dummy storage env options, so we can tell all the different paths using (var options = StorageEnvironmentOptions.CreateMemoryOnly()) { using (var store = GetDocumentStore()) { store.Maintenance.Send(new CreateSampleDataOperation()); // the database loads after all indexes are loaded var documentDatabase = await Server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); var lines = File.ReadAllLines(outputFile); Assert.True(lines.Length == 6); Assert.True(lines[0].Contains($"{DirectoryExecUtils.EnvironmentType.System} {SystemDbName} {options.BasePath} {options.TempPath} {options.JournalPath}")); Assert.True(lines[1].Contains($"{DirectoryExecUtils.EnvironmentType.Configuration} {store.Database} {options.BasePath} {options.TempPath} {options.JournalPath}")); Assert.True(lines[2].Contains($"{DirectoryExecUtils.EnvironmentType.Database} {store.Database} {options.BasePath} {options.TempPath} {options.JournalPath}")); var indexes = documentDatabase.IndexStore.GetIndexes().ToArray(); Assert.True(indexes.Length == 3); // The indexes order in the IndexStore don't match the order of storage env creation and we need a one-to-one match. var matches = lines.ToList().GetRange(3, 3); foreach (var index in indexes) { var expected = $"{DirectoryExecUtils.EnvironmentType.Index} {store.Database} {index._environment.Options.BasePath} {index._environment.Options.TempPath} {index._environment.Options.JournalPath}"; var indexToRemove = matches.FindIndex(str => str.Contains(expected)); if (indexToRemove != -1) { matches.RemoveAt(indexToRemove); } } Assert.Equal(0, matches.Count); } } }
protected DocumentStore GetDocumentStore(Options options = null, [CallerMemberName] string caller = null) { try { lock (_getDocumentStoreSync) { options = options ?? Options.Default; var serverToUse = options.Server ?? Server; var name = GetDatabaseName(caller); if (options.ModifyDatabaseName != null) { name = options.ModifyDatabaseName(name) ?? name; } var hardDelete = true; var runInMemory = true; var pathToUse = options.Path; if (pathToUse == null) { pathToUse = NewDataPath(name); } else { hardDelete = false; runInMemory = false; } var doc = new DatabaseRecord(name) { Settings = { [RavenConfiguration.GetKey(x => x.Replication.ReplicationMinimalHeartbeat)] = "1", [RavenConfiguration.GetKey(x => x.Replication.RetryReplicateAfter)] = "1", [RavenConfiguration.GetKey(x => x.Core.RunInMemory)] = runInMemory.ToString(), [RavenConfiguration.GetKey(x => x.Core.DataDirectory)] = pathToUse, [RavenConfiguration.GetKey(x => x.Core.ThrowIfAnyIndexCannotBeOpened)] = "true", [RavenConfiguration.GetKey(x => x.Indexing.MinNumberOfMapAttemptsAfterWhichBatchWillBeCanceledIfRunningLowOnMemory)] = int.MaxValue.ToString() } }; options.ModifyDatabaseRecord?.Invoke(doc); var store = new DocumentStore { Urls = UseFiddler(serverToUse.WebUrl), Database = name, Certificate = options.ClientCertificate }; options.ModifyDocumentStore?.Invoke(store); //This gives too much error details in most cases, we don't need this now store.RequestExecutorCreated += (sender, executor) => { executor.AdditionalErrorInformation += sb => sb.AppendLine().Append(GetLastStatesFromAllServersOrderedByTime()); }; store.Initialize(); if (options.CreateDatabase) { foreach (var server in Servers) { using (server.ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context)) { context.OpenReadTransaction(); if (server.ServerStore.Cluster.Read(context, Constants.Documents.Prefix + name) != null) { throw new InvalidOperationException($"Database '{name}' already exists"); } } } DatabasePutResult result; if (options.AdminCertificate != null) { using (var adminStore = new DocumentStore { Urls = UseFiddler(serverToUse.WebUrl), Database = name, Certificate = options.AdminCertificate }.Initialize()) { result = adminStore.Maintenance.Server.Send(new CreateDatabaseOperation(doc, options.ReplicationFactor)); } } else { result = store.Maintenance.Server.Send(new CreateDatabaseOperation(doc, options.ReplicationFactor)); } Assert.True(result.RaftCommandIndex > 0); //sanity check store.Urls = result.NodesAddedTo.SelectMany(UseFiddler).ToArray(); var timeout = TimeSpan.FromMinutes(Debugger.IsAttached ? 5 : 1); var task = WaitForRaftIndexToBeAppliedInCluster(result.RaftCommandIndex, timeout); task.ConfigureAwait(false).GetAwaiter().GetResult(); } store.BeforeDispose += (sender, args) => { if (CreatedStores.TryRemove(store) == false) { return; // can happen if we are wrapping the store inside sharded one } foreach (var server in Servers) { if (server.Disposed) { continue; } var serverUrl = UseFiddler(server.WebUrl); if (store.Urls.Any(url => serverUrl.Contains(url)) == false) { continue; } try { var databaseTask = server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(name, options.IgnoreDisabledDatabase); if (databaseTask != null && databaseTask.IsCompleted == false) { // if we are disposing store before database had chance to load then we need to wait databaseTask.Wait(); } } catch (DatabaseDisabledException) { continue; } catch (DatabaseNotRelevantException) { continue; } if (options.DeleteDatabaseOnDispose) { DeleteDatabaseResult result; try { if (options.AdminCertificate != null) { using (var adminStore = new DocumentStore { Urls = UseFiddler(serverToUse.WebUrl), Database = name, Certificate = options.AdminCertificate }.Initialize()) { result = adminStore.Maintenance.Server.Send(new DeleteDatabasesOperation(name, hardDelete)); } } else { result = store.Maintenance.Server.Send(new DeleteDatabasesOperation(name, hardDelete)); } } catch (DatabaseDoesNotExistException) { continue; } catch (NoLeaderException) { continue; } server.ServerStore.Cluster.WaitForIndexNotification(result.RaftCommandIndex).ConfigureAwait(false).GetAwaiter().GetResult(); } } }; CreatedStores.Add(store); return(store); } } catch (TimeoutException te) { throw new TimeoutException($"{te.Message} {Environment.NewLine} {te.StackTrace}{Environment.NewLine}Servers states:{Environment.NewLine}{GetLastStatesFromAllServersOrderedByTime()}"); } }
public async Task RequiredForNextPolicyTest() { DefaultClusterSettings[RavenConfiguration.GetKey(x => x.Tombstones.CleanupInterval)] = 1.ToString(); var cluster = await CreateRaftCluster(3, watcherCluster : true); using (var store = GetDocumentStore(new Options { Server = cluster.Leader, ReplicationFactor = 3, RunInMemory = false })) { var retention = TimeSpan.FromSeconds(180); var raw = new RawTimeSeriesPolicy(retention); var config = new TimeSeriesConfiguration { Collections = new Dictionary <string, TimeSeriesCollectionConfiguration> { ["Users"] = new TimeSeriesCollectionConfiguration { RawPolicy = raw, Policies = new List <TimeSeriesPolicy> { new TimeSeriesPolicy("ByMinute", TimeSpan.FromSeconds(60)) } } }, PolicyCheckFrequency = TimeSpan.FromSeconds(1) }; await store.Maintenance.SendAsync(new ConfigureTimeSeriesOperation(config)); var now = new DateTime(2021, 6, 1, 18, 45, 0, 999, DateTimeKind.Utc); foreach (var server in Servers) { var database = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); database.Time.UtcDateTime = () => now; } var baseline = now.AddSeconds(-120); using (var session = store.OpenSession()) { var id = "users/karmel/0"; session.Store(new User { Name = "Karmel" }, id); for (int i = 0; i < 120; i++) { session.TimeSeriesFor(id, "Heartrate") .Append(baseline.AddSeconds(i), i); } session.SaveChanges(); } using (var session = store.OpenSession()) { session.Store(new User(), "marker"); session.SaveChanges(); Assert.True(await WaitForDocumentInClusterAsync <User>(cluster.Nodes, store.Database, "marker", null, TimeSpan.FromSeconds(15))); } foreach (var server in Servers) { var database = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); database.Time.UtcDateTime = () => now.AddMinutes(3); } var sp = Stopwatch.StartNew(); var check = true; while (check) { Assert.True(sp.Elapsed < retention.Add(TimeSpan.FromMinutes(-2)), $"too long has passed {sp.Elapsed}, retention is {retention}"); await Task.Delay(200); check = false; foreach (var server in Servers) { var database = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database); var tss = database.DocumentsStorage.TimeSeriesStorage; using (database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext ctx)) using (ctx.OpenReadTransaction()) { var id = $"users/karmel/0"; var stats = tss.Stats.GetStats(ctx, id, "Heartrate"); TimeSeriesReader reader; if (stats == default || stats.Count == 0) { var name = config.Collections["Users"].Policies[0].GetTimeSeriesName("Heartrate"); stats = tss.Stats.GetStats(ctx, id, name); reader = tss.GetReader(ctx, id, name, DateTime.MinValue, DateTime.MaxValue); Assert.True(stats.Count > 0); Assert.Equal(stats.Start, reader.First().Timestamp, RavenTestHelper.DateTimeComparer.Instance); Assert.Equal(stats.End, reader.Last().Timestamp, RavenTestHelper.DateTimeComparer.Instance); continue; } check = true; reader = tss.GetReader(ctx, id, "Heartrate", DateTime.MinValue, DateTime.MaxValue); Assert.Equal(stats.Start, reader.First().Timestamp, RavenTestHelper.DateTimeComparer.Instance); Assert.Equal(stats.End, reader.Last().Timestamp, RavenTestHelper.DateTimeComparer.Instance); } } } } }
private void ValidateAllowedDestinations() { if (AllowedDestinations == null) { return; } if (AllowedDestinations.Contains("None", StringComparer.OrdinalIgnoreCase)) { if (AllowedDestinations.Length > 1) { throw new ArgumentException($"If you specify \"None\" under '{RavenConfiguration.GetKey(x => x.Backup.AllowedDestinations)}' then it must be the only value."); } return; } foreach (var dest in AllowedDestinations) { if (_allDestinations.Contains(dest, StringComparer.OrdinalIgnoreCase)) { continue; } throw new ArgumentException($"The destination '{dest}' defined in the configuration under '{RavenConfiguration.GetKey(x => x.Backup.AllowedDestinations)}' is unknown. Make sure to use the following destinations: {string.Join(", ", _allDestinations)}."); } }
public async Task AwaitAsyncPatchByIndexShouldWork() { using (var store = GetDocumentStore(modifyDatabaseDocument: document => document.Settings[RavenConfiguration.GetKey(x => x.Core.RunInMemory)] = "false")) { RavenQueryStatistics stats; using (var session = store.OpenSession()) { session.Query <User>() .Statistics(out stats) .Where(x => x.Name == "John") .ToList(); } using (var session = store.OpenAsyncSession()) { for (int i = 0; i < 30; i++) { await session.StoreAsync(new User { Name = "First #" + i, LastName = "Last #" + i }, "users/" + i); await session.SaveChangesAsync(); } } WaitForIndexing(store, timeout: TimeSpan.FromMinutes(5)); JsonOperationContext context; store.GetRequestExecuter(store.DefaultDatabase).ContextPool.AllocateOperationContext(out context); var patchByIndexOperation = new PatchByIndexOperation(context); var patchCommand = patchByIndexOperation.CreateRequest(stats.IndexName, new IndexQuery { Query = string.Empty }, null, new PatchRequest { Script = "this.FullName = this.FirstName + ' ' + this.LastName;" }, store); if (patchCommand != null) { await store.GetRequestExecuter(store.DefaultDatabase).ExecuteAsync(patchCommand, context); } using (var db = store.OpenAsyncSession()) { var lastUser = await db.LoadAsync <User>("users/29"); Assert.NotNull(lastUser.FullName); } } }