Example #1
0
        public async Task CanDeleteEtl()
        {
            using (var store = GetDocumentStore())
            {
                var database = await GetDatabase(store.Database);

                var configuration = new RavenEtlConfiguration()
                {
                    ConnectionStringName = "test",
                    Name       = "aaa",
                    Transforms =
                    {
                        new Transformation()
                        {
                            Collections ={ "Users"                   }
                        }
                    }
                };

                var result = AddEtl(store, configuration, new RavenConnectionString()
                {
                    Name     = "test",
                    Url      = "http://127.0.0.1:8080",
                    Database = "Northwind",
                });

                store.Admin.Server.Send(new DeleteOngoingTaskOperation(database.Name, result.TaskId, OngoingTaskType.RavenEtl));
            }
        }
Example #2
0
        public void Error_if_script_does_not_contain_any_loadTo_method()
        {
            var config = new RavenEtlConfiguration
            {
                Name = "test",
                ConnectionStringName = "test",
                Transforms           =
                {
                    new Transformation
                    {
                        Name        = "test",
                        Collections ={ "Users"                   },
                        Script      = @"this.Name = 'aaa';"
                    }
                }
            };

            config.Initialize(new RavenConnectionString()
            {
                Database = "Foo", TopologyDiscoveryUrls = new[] { "http://localhost:8080" }
            });

            List <string> errors;

            config.Validate(out errors);

            Assert.Equal(1, errors.Count);

            Assert.Equal("No `loadTo[CollectionName]` method call found in 'test' script", errors[0]);
        }
Example #3
0
        public void Can_put_space_after_loadTo_method_in_script()
        {
            var config = new RavenEtlConfiguration
            {
                Name = "test",
                ConnectionStringName = "test",
                Transforms           =
                {
                    new Transformation
                    {
                        Name        = "test",
                        Collections ={ "Users"                   },
                        Script      = @"loadToUsers (this);"
                    }
                }
            };

            config.Initialize(new RavenConnectionString()
            {
                Database = "Foo", TopologyDiscoveryUrls = new [] { "http://localhost:8080" }
            });

            List <string> errors;

            config.Validate(out errors);

            Assert.Equal(0, errors.Count);

            var collections = config.Transforms[0].GetCollectionsFromScript();

            Assert.Equal(1, collections.Length);
            Assert.Equal("Users", collections[0]);
        }
Example #4
0
        public void CanDeleteEtl()
        {
            using (var store = GetDocumentStore())
            {
                var configuration = new RavenEtlConfiguration
                {
                    ConnectionStringName = "test",
                    Name       = "aaa",
                    Transforms =
                    {
                        new Transformation
                        {
                            Name        = "S1",
                            Collections ={ "Users"                   }
                        }
                    }
                };

                var result = AddEtl(store, configuration, new RavenConnectionString
                {
                    Name = "test",
                    TopologyDiscoveryUrls = new[] { "http://127.0.0.1:8080" },
                    Database = "Northwind",
                });

                store.Maintenance.Send(new DeleteOngoingTaskOperation(result.TaskId, OngoingTaskType.RavenEtl));

                var ongoingTask = store.Maintenance.Send(new GetOngoingTaskInfoOperation(result.TaskId, OngoingTaskType.RavenEtl));

                Assert.Null(ongoingTask);
            }
        }
Example #5
0
        public async Task CanDisableEtl()
        {
            using (var store = GetDocumentStore())
            {
                var database = await GetDatabase(store.Database);

                var configuration = new RavenEtlConfiguration()
                {
                    ConnectionStringName = "test",
                    Name       = "aaa",
                    Transforms =
                    {
                        new Transformation()
                        {
                            Collections ={ "Users"                   }
                        }
                    }
                };

                var result = AddEtl(store, configuration, new RavenConnectionString()
                {
                    Name     = "test",
                    Url      = "http://127.0.0.1:8080",
                    Database = "Northwind",
                });


                store.Admin.Server.Send(new ToggleTaskStateOperation(database.Name, result.TaskId, OngoingTaskType.RavenEtl, true));

                var ongoingTask = store.Admin.Server.Send(new GetOngoingTaskInfoOperation(store.Database, result.TaskId, OngoingTaskType.RavenEtl));

                Assert.Equal(OngoingTaskState.Disabled, ongoingTask.TaskState);
            }
        }
Example #6
0
        public void CanDisableEtl()
        {
            using (var store = GetDocumentStore())
            {
                var configuration = new RavenEtlConfiguration
                {
                    ConnectionStringName = "test",
                    Name       = "aaa",
                    Transforms =
                    {
                        new Transformation
                        {
                            Name        = "S1",
                            Collections ={ "Users"                   }
                        }
                    }
                };

                var result = AddEtl(store, configuration, new RavenConnectionString
                {
                    Name = "test",
                    TopologyDiscoveryUrls = new[] { "http://127.0.0.1:8080" },
                    Database = "Northwind",
                });

                var toggleResult = store.Maintenance.Send(new ToggleOngoingTaskStateOperation(result.TaskId, OngoingTaskType.RavenEtl, true));
                Assert.NotNull(toggleResult);
                Assert.True(toggleResult.RaftCommandIndex > 0);
                Assert.True(toggleResult.TaskId > 0);

                var ongoingTask = store.Maintenance.Send(new GetOngoingTaskInfoOperation(result.TaskId, OngoingTaskType.RavenEtl));
                Assert.Equal(OngoingTaskState.Disabled, ongoingTask.TaskState);
            }
        }
Example #7
0
        public void Error_if_script_has_both_apply_to_all_documents_and_collections_specified()
        {
            var config = new RavenEtlConfiguration
            {
                Name = "test",
                ConnectionStringName = "test",
                Transforms           =
                {
                    new Transformation
                    {
                        Name = "test",
                        ApplyToAllDocuments = true,
                        Collections         = { "Users" }
                    }
                }
            };

            config.Initialize(new RavenConnectionString()
            {
                Database = "Foo", TopologyDiscoveryUrls = new[] { "http://localhost:8080" }
            });

            List <string> errors;

            config.Validate(out errors);

            Assert.Equal(1, errors.Count);

            Assert.Equal("Collections cannot be specified when ApplyToAllDocuments is set", errors[0]);
        }
Example #8
0
        internal static async Task <AddEtlOperationResult> AddEtl(IDocumentStore source, string destination, string[] urls, string mentor)
        {
            var connectionStringName = $"RavenEtl_From{source.Database}_To{destination}";
            var config = new RavenEtlConfiguration()
            {
                Name = connectionStringName,
                ConnectionStringName    = connectionStringName,
                LoadRequestTimeoutInSec = 10,
                MentorNode = mentor,
                Transforms = new List <Transformation>
                {
                    new Transformation
                    {
                        Name = $"ETL : {connectionStringName}",
                        ApplyToAllDocuments = true,
                        IsEmptyScript       = true
                    }
                }
            };
            var connectionString = new RavenConnectionString
            {
                Name     = connectionStringName,
                Database = destination,
                TopologyDiscoveryUrls = urls,
            };

            var result = await source.Maintenance.SendAsync(new PutConnectionStringOperation <RavenConnectionString>(connectionString));

            Assert.NotNull(result.RaftCommandIndex);

            return(await source.Maintenance.SendAsync(new AddEtlOperation <RavenConnectionString>(config)));
        }
Example #9
0
        public async Task CanUpdateEtl()
        {
            using (var store = GetDocumentStore())
            {
                var database = await GetDatabase(store.Database);

                var configuration = new RavenEtlConfiguration()
                {
                    ConnectionStringName = "test",
                    Name       = "aaa",
                    Transforms =
                    {
                        new Transformation()
                        {
                            Collections ={ "Users"                   }
                        }
                    }
                };

                var result = AddEtl(store, configuration, new RavenConnectionString()
                {
                    Name     = "test",
                    Url      = "http://127.0.0.1:8080",
                    Database = "Northwind",
                });

                configuration.Transforms[0].Disabled = true;

                store.Admin.Server.Send(new UpdateEtlOperation <RavenConnectionString>(result.TaskId, configuration, database.Name));
            }
        }
Example #10
0
        public async Task AutoNamingAlgorithmOfOngoingTasksShouldTakeNameAlreadyExistsIntoAccount()
        {
            using (var store = GetDocumentStore())
            {
                var dbName = $"db/{Guid.NewGuid()}";
                var csName = $"cs/{Guid.NewGuid()}";

                await store.Maintenance.SendAsync(new PutConnectionStringOperation <RavenConnectionString>(new RavenConnectionString
                {
                    Name = csName,
                    Database = dbName,
                    TopologyDiscoveryUrls = new[] { "http://127.0.0.1:12345" }
                }));

                await store.Maintenance.SendAsync(new UpdateExternalReplicationOperation(new ExternalReplication(dbName, csName)));

                await store.Maintenance.SendAsync(new UpdateExternalReplicationOperation(new ExternalReplication(dbName, csName)));

                var backupConfig = new PeriodicBackupConfiguration
                {
                    LocalSettings = new LocalSettings
                    {
                        FolderPath = NewDataPath(suffix: "BackupFolder")
                    },
                    AzureSettings = new AzureSettings
                    {
                        StorageContainer = "abc"
                    },
                    FullBackupFrequency        = "* */1 * * *",
                    IncrementalBackupFrequency = "* */2 * * *",
                    Disabled = true
                };

                await store.Maintenance.SendAsync(new UpdatePeriodicBackupOperation(backupConfig));

                await store.Maintenance.SendAsync(new UpdatePeriodicBackupOperation(backupConfig));

                var etlConfiguration = new RavenEtlConfiguration()
                {
                    ConnectionStringName = csName,
                    Transforms           =
                    {
                        new Transformation()
                        {
                            Name        = "loadAll",
                            Collections ={ "Users"                   },
                            Script      = "loadToUsers(this)"
                        }
                    }
                };

                await store.Maintenance.SendAsync(new AddEtlOperation <RavenConnectionString>(etlConfiguration));

                await store.Maintenance.SendAsync(new AddEtlOperation <RavenConnectionString>(etlConfiguration));
            }
        }
Example #11
0
        public void Should_throw_when_transforms_is_empty()
        {
            var configuration = new RavenEtlConfiguration {
                ConnectionStringName = "test", Name = "myConfig", MentorNode = "A", Transforms = new List <Transformation>()
            };

            configuration.Initialize(new RavenConnectionString());
            var e = Assert.Throws <InvalidOperationException>(() => configuration.Validate(out List <string> _));

            Assert.Equal($"'{nameof(RavenEtlConfiguration.Transforms)}' list cannot be empty.", e.Message);
        }
Example #12
0
        public void GetRavenEtlTaskInfo()
        {
            var etlConfiguration = new RavenEtlConfiguration()
            {
                Name = "test",
                ConnectionStringName = "cs",
                Transforms           =
                {
                    new Transformation()
                    {
                        Name        = "loadAll",
                        Collections ={ "Users"                   },
                        Script      = "loadToUsers(this)"
                    }
                }
            };

            using (var store = GetDocumentStore())
            {
                var result = store.Maintenance.Send(new PutConnectionStringOperation <RavenConnectionString>(new RavenConnectionString
                {
                    Name = "cs",
                    TopologyDiscoveryUrls = new[] { "http://127.0.0.1:8080" },
                    Database = "Northwind",
                }));
                Assert.NotNull(result.RaftCommandIndex);

                var ravenEtlResult = store.Maintenance.Send(new AddEtlOperation <RavenConnectionString>(etlConfiguration));

                var taskId = ravenEtlResult.TaskId;

                var op        = new GetOngoingTaskInfoOperation(taskId, OngoingTaskType.RavenEtl);
                var etlResult = (OngoingTaskRavenEtlDetails)store.Maintenance.Send(op);

                Assert.Equal("cs", etlResult.Configuration.ConnectionStringName);
                Assert.Equal("test", etlResult.Configuration.Name);
                Assert.Equal("loadAll", etlResult.Configuration.Transforms[0].Name);
                Assert.Equal("loadToUsers(this)", etlResult.Configuration.Transforms[0].Script);
                Assert.Equal("Users", etlResult.Configuration.Transforms[0].Collections[0]);
                Assert.Equal(etlConfiguration.Name, etlResult?.TaskName);

                op        = new GetOngoingTaskInfoOperation("test", OngoingTaskType.RavenEtl);
                etlResult = (OngoingTaskRavenEtlDetails)store.Maintenance.Send(op);

                Assert.Equal("cs", etlResult.Configuration.ConnectionStringName);
                Assert.Equal(taskId, etlResult.TaskId);
                Assert.Equal("loadAll", etlResult.Configuration.Transforms[0].Name);
                Assert.Equal("loadToUsers(this)", etlResult.Configuration.Transforms[0].Script);
                Assert.Equal("Users", etlResult.Configuration.Transforms[0].Collections[0]);
                Assert.Equal(etlConfiguration.Name, etlResult?.TaskName);
            }
        }
Example #13
0
        public void EtlTaskDeletionShouldDeleteItsState()
        {
            using (var src = GetDocumentStore())
                using (var dest = GetDocumentStore())
                {
                    using (var session = src.OpenSession())
                    {
                        session.Store(new User());
                        session.SaveChanges();
                    }

                    var etlDone = WaitForEtl(src, (n, s) => s.LoadSuccesses > 0);

                    var configuration = new RavenEtlConfiguration()
                    {
                        ConnectionStringName = "test",
                        Name       = "myConfiguration",
                        Transforms =
                        {
                            new Transformation()
                            {
                                Name        = "allUsers",
                                Collections ={ "Users"                   }
                            }
                        }
                    };

                    var result = AddEtl(src, configuration, new RavenConnectionString()
                    {
                        Name = "test",
                        TopologyDiscoveryUrls = dest.Urls,
                        Database = dest.Database,
                    });

                    Assert.True(etlDone.Wait(TimeSpan.FromMinutes(1)));

                    src.Maintenance.Send(new DeleteOngoingTaskOperation(result.TaskId, OngoingTaskType.RavenEtl));

                    etlDone.Reset();

                    AddEtl(src, configuration, new RavenConnectionString()
                    {
                        Name = "test",
                        TopologyDiscoveryUrls = dest.Urls,
                        Database = dest.Database,
                    });

                    Assert.True(etlDone.Wait(TimeSpan.FromMinutes(1)));
                }
        }
Example #14
0
        public async Task CanGetBatchStopReasonFromEtlPerformanceStats()
        {
            using (var src = GetDocumentStore())
                using (var dst = GetDocumentStore())
                {
                    var configuration = new RavenEtlConfiguration
                    {
                        ConnectionStringName = "test",
                        Name       = "aaa",
                        Transforms =
                        {
                            new Transformation
                            {
                                Name        = "S1",
                                Collections ={ "Users"                   }
                            }
                        }
                    };

                    AddEtl(src, configuration, new RavenConnectionString
                    {
                        Name = "test",
                        TopologyDiscoveryUrls = dst.Urls,
                        Database = dst.Database,
                    });

                    var etlDone = WaitForEtl(src, (_, statistics) => statistics.LoadSuccesses == 10);

                    using (var session = src.OpenAsyncSession())
                    {
                        for (int i = 0; i < 10; i++)
                        {
                            await session.StoreAsync(new User());
                        }

                        await session.SaveChangesAsync();
                    }

                    Assert.True(etlDone.Wait(TimeSpan.FromSeconds(10)));

                    var database = await Databases.GetDocumentDatabaseInstanceFor(src);

                    var etlProcess  = database.EtlLoader.Processes.First();
                    var performance = etlProcess.GetPerformanceStats();

                    Assert.Contains("Successfully finished loading all batch items", performance.Select(p => p.BatchStopReason));
                    Assert.Contains("No more items to process", performance.Select(p => p.BatchTransformationCompleteReason));
                }
        }
Example #15
0
        public void ShouldThrowOnInvalidConfigOnUpdate()
        {
            using (var src = GetDocumentStore())
                using (var dest = GetDocumentStore())
                {
                    using (var session = src.OpenSession())
                    {
                        session.Store(new Doc {
                            Id = "doc-1", StrVal = "doc-1", StrVal2 = "doc-1"
                        });
                        session.SaveChanges();
                    }

                    var putResult = src.Maintenance.Send(new PutConnectionStringOperation <RavenConnectionString>(new RavenConnectionString
                    {
                        Name = "test", TopologyDiscoveryUrls = dest.Urls, Database = dest.Database,
                    }));

                    var configuration = new RavenEtlConfiguration()
                    {
                        ConnectionStringName = "test",
                        Name       = "myConfiguration",
                        Transforms = { new Transformation()
                                       {
                                           Name = "allDocs", Collections ={ "Docs"                                     }, Script = @"loadToDocs({ StrVal: this.StrVal });",
                                       } }
                    };

                    var addResult = src.Maintenance.Send(new AddEtlOperation <RavenConnectionString>(configuration));

                    var configuration2 = new RavenEtlConfiguration()
                    {
                        ConnectionStringName = "test",
                        Name       = "myConfiguration",
                        Transforms =
                        {
                            new Transformation()
                            {
                                Name = "allDocs", Collections ={ "Docs"                                     }, Script = @"loadToDocs({ StrVal: this.StrVal, StrVal2: this.StrVal2 });", ApplyToAllDocuments = true,
                            }
                        }
                    };

                    var ex = Assert.Throws <RavenException>(() => src.Maintenance.Send(new UpdateEtlOperation <RavenConnectionString>(addResult.TaskId, configuration2)));

                    Assert.Contains("Collections cannot be specified when ApplyToAllDocuments is set. Script name: 'allDocs'", ex.Message);
                }
        }
Example #16
0
        public void EntersFallbackModeIfCantConnectTheDestination()
        {
            using (var src = GetDocumentStore())
            {
                using (var store = src.OpenSession())
                {
                    store.Store(new User());

                    store.SaveChanges();
                }

                var configuration = new RavenEtlConfiguration()
                {
                    ConnectionStringName = "test",
                    Name       = "aaa",
                    Transforms =
                    {
                        new Transformation()
                        {
                            Collections ={ "Users"                   },
                            Name        = "test"
                        }
                    }
                };

                AddEtl(src, configuration, new RavenConnectionString
                {
                    Name = "test",
                    TopologyDiscoveryUrls = new [] { "http://abc.localhost:1234" },
                    Database = "test",
                });

                var process = GetDatabase(src.Database).Result.EtlLoader.Processes[0];

                Assert.True(SpinWait.SpinUntil(() =>
                {
                    if (process.FallbackTime != null)
                    {
                        return(true);
                    }

                    Thread.Sleep(100);

                    return(false);
                }, TimeSpan.FromMinutes(1)));
            }
        }
Example #17
0
        public void CanResetEtl()
        {
            using (var src = GetDocumentStore())
                using (var dest = GetDocumentStore())
                {
                    using (var session = src.OpenSession())
                    {
                        session.Store(new User());
                        session.SaveChanges();
                    }

                    var runs = 0;

                    var etlDone = WaitForEtl(src, (n, s) => s.LoadSuccesses > 0);

                    var resetDone = WaitForEtl(src, (n, statistics) => ++ runs >= 2);

                    var configuration = new RavenEtlConfiguration()
                    {
                        ConnectionStringName = "test",
                        Name       = "myConfiguration",
                        Transforms =
                        {
                            new Transformation()
                            {
                                Name        = "allUsers",
                                Collections ={ "Users"                   }
                            }
                        }
                    };

                    AddEtl(src, configuration, new RavenConnectionString()
                    {
                        Name = "test",
                        TopologyDiscoveryUrls = dest.Urls,
                        Database = dest.Database,
                    });

                    Assert.True(etlDone.Wait(TimeSpan.FromMinutes(1)));

                    src.Admin.Server.Send(new ResetEtlOperation("myConfiguration", "allUsers", src.Database));

                    Assert.True(resetDone.Wait(TimeSpan.FromMinutes(1)));
                }
        }
Example #18
0
        public async Task CanResetEtl3()
        {
            using (var src = GetDocumentStore())
                using (var dest = GetDocumentStore())
                {
                    var configuration = new RavenEtlConfiguration()
                    {
                        ConnectionStringName = "test",
                        Name       = "myConfiguration",
                        Transforms = { new Transformation()
                                       {
                                           Name = "allUsers", Collections ={ "Users"                                      }
                                       } }
                    };

                    AddEtl(src, configuration, new RavenConnectionString {
                        Name = "test", TopologyDiscoveryUrls = dest.Urls, Database = dest.Database,
                    });

                    var t = Task.Run(async() =>
                    {
                        for (int i = 0; i < 100; i++)
                        {
                            await src.Maintenance.SendAsync(new ResetEtlOperation("myConfiguration", "allUsers"));
                            await Task.Delay(100);
                        }
                    });

                    var indexes = new List <Task>();
                    for (int i = 0; i < 100; i++)
                    {
                        var index = new Index($"test{i}");
                        indexes.Add(index.ExecuteAsync(src));
                    }

                    await Task.WhenAll(indexes);

                    await t;

                    var record = await src.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(src.Database));

                    Assert.Equal(100, record.Indexes.Count);
                }
        }
Example #19
0
        public async Task CanUpdateEtl()
        {
            using (var store = GetDocumentStore())
            {
                var database = await GetDatabase(store.Database);

                var configuration = new RavenEtlConfiguration()
                {
                    ConnectionStringName = "test",
                    Name       = "aaa",
                    Transforms =
                    {
                        new Transformation()
                        {
                            Collections ={ "Users"                   }
                        },
                        new Transformation()
                        {
                            Collections ={ "Users"                   }
                        }
                    }
                };

                var result = AddEtl(store, configuration, new RavenConnectionString()
                {
                    Name = "test",
                    TopologyDiscoveryUrls = new[] { "http://127.0.0.1:8080" },
                    Database = "Northwind",
                });

                configuration.Transforms[0].Disabled = true;

                var update = store.Admin.Server.Send(new UpdateEtlOperation <RavenConnectionString>(result.TaskId, configuration, database.Name));

                var ongoingTask = store.Admin.Server.Send(new GetOngoingTaskInfoOperation(store.Database, update.TaskId, OngoingTaskType.RavenEtl));

                Assert.Equal(OngoingTaskState.PartiallyEnabled, ongoingTask.TaskState);
            }
        }
Example #20
0
        public void Error_if_script_does_not_contain_any_loadTo_method_and_isnt_empty()
        {
            var config = new RavenEtlConfiguration
            {
                Name = "test",
                ConnectionStringName = "test",
                Transforms           =
                {
                    new Transformation
                    {
                        Name        = "test",
                        Collections ={ "Users"                   },
                        Script      = @"
                        
                        this.Name = 'aaa'; // we have some modification but we don't call loadTo method so it should error

                        function deleteDocumentsOfUsersBehavior(docId) {
                            return false;
                        }
                    "
                    }
                }
            };

            config.Initialize(new RavenConnectionString()
            {
                Database = "Foo", TopologyDiscoveryUrls = new[] { "http://localhost:8080" }
            });

            List <string> errors;

            config.Validate(out errors);

            Assert.Equal(1, errors.Count);

            Assert.Equal("No `loadTo<CollectionName>()` method call found in 'test' script", errors[0]);
        }
Example #21
0
        private IEnumerable <EtlProcess> GetRelevantProcesses <T, TConnectionString>(List <T> configurations, HashSet <string> uniqueNames) where T : EtlConfiguration <TConnectionString> where TConnectionString : ConnectionString
        {
            foreach (var config in configurations)
            {
                SqlEtlConfiguration   sqlConfig   = null;
                RavenEtlConfiguration ravenConfig = null;

                var connectionStringNotFound = false;

                switch (config.EtlType)
                {
                case EtlType.Raven:
                    ravenConfig = config as RavenEtlConfiguration;
                    if (_databaseRecord.RavenConnectionStrings.TryGetValue(config.ConnectionStringName, out var ravenConnection))
                    {
                        ravenConfig.Initialize(ravenConnection);
                    }
                    else
                    {
                        connectionStringNotFound = true;
                    }

                    break;

                case EtlType.Sql:
                    sqlConfig = config as SqlEtlConfiguration;
                    if (_databaseRecord.SqlConnectionStrings.TryGetValue(config.ConnectionStringName, out var sqlConnection))
                    {
                        sqlConfig.Initialize(sqlConnection);
                    }
                    else
                    {
                        connectionStringNotFound = true;
                    }

                    break;

                default:
                    ThrownUnknownEtlConfiguration(config.GetType());
                    break;
                }

                if (connectionStringNotFound)
                {
                    LogConfigurationError(config,
                                          new List <string>
                    {
                        $"Connection string named '{config.ConnectionStringName}' was not found for {config.EtlType} ETL"
                    });

                    continue;
                }

                if (config.Disabled)
                {
                    continue;
                }

                if (ValidateConfiguration(config, uniqueNames) == false)
                {
                    continue;
                }

                var processState  = GetProcessState(config.Transforms, _database, config.Name);
                var whoseTaskIsIt = _database.WhoseTaskIsIt(_databaseRecord.Topology, config, processState);
                if (whoseTaskIsIt != _serverStore.NodeTag)
                {
                    continue;
                }

                foreach (var transform in config.Transforms)
                {
                    if (transform.Disabled)
                    {
                        continue;
                    }

                    EtlProcess process = null;

                    if (sqlConfig != null)
                    {
                        process = new SqlEtl(transform, sqlConfig, _database, _serverStore);
                    }

                    if (ravenConfig != null)
                    {
                        process = new RavenEtl(transform, ravenConfig, _database, _serverStore);
                    }

                    yield return(process);
                }
            }
        }
Example #22
0
        public async Task Can_fail_over_etl_task()
        {
            var(nodes, leader) = await CreateRaftCluster(3, customSettings :
                                                         new Dictionary <string, string>
            {
                { "ETL.MaxNumberOfExtractedDocuments", "5" }
            });

            var database = GetDatabaseName();

            await CreateDatabaseInClusterInner(new DatabaseRecord(database), 3, leader.WebUrl, null);

            using (var destination = GetDocumentStore())
                using (var source = new DocumentStore
                {
                    Database = database,
                    Urls = new[] { leader.WebUrl }
                }.Initialize())
                {
                    string lastDocumentId = null;
                    using (var bulkInsert = source.BulkInsert())
                    {
                        for (var i = 0; i < 10; i++)
                        {
                            lastDocumentId = i.ToString();
                            await bulkInsert.StoreAsync(new User(), lastDocumentId);
                        }
                    }

                    var putResult = await source.Maintenance.SendAsync(new PutConnectionStringOperation <RavenConnectionString>(new RavenConnectionString
                    {
                        Name = "test",
                        TopologyDiscoveryUrls = new[] { destination.Urls.First() },
                        Database = destination.Database,
                    }));

                    Assert.NotNull(putResult.RaftCommandIndex);

                    var configuration = new RavenEtlConfiguration
                    {
                        ConnectionStringName = "test",
                        Name       = "myConfiguration",
                        Transforms =
                        {
                            new Transformation
                            {
                                Name        = "allDocs",
                                Collections ={ "Users"                   }
                            }
                        },
                        MentorNode = "A"
                    };

                    var addResult = await source.Maintenance.SendAsync(new AddEtlOperation <RavenConnectionString>(configuration));

                    Assert.NotNull(addResult.RaftCommandIndex);
                    Assert.True(WaitForDocument(destination, lastDocumentId));

                    configuration.MentorNode = "B";
                    var updateResult = await source.Maintenance.SendAsync(new UpdateEtlOperation <RavenConnectionString>(addResult.TaskId, configuration));

                    Assert.NotNull(updateResult.RaftCommandIndex);

                    using (var session = source.OpenAsyncSession())
                    {
                        var user = new User();
                        await session.StoreAsync(user);

                        lastDocumentId = user.Id;
                        await session.SaveChangesAsync();
                    }

                    Assert.True(WaitForDocument(destination, lastDocumentId));
                }
        }
Example #23
0
        public async Task ReplicateFromSingleSource()
        {
            var srcDb = "ReplicateFromSingleSourceSrc";
            var dstDb = "ReplicateFromSingleSourceDst";

            var(_, srcRaft) = await CreateRaftCluster(3);

            var(_, dstRaft) = await CreateRaftCluster(1);

            var srcNodes = await CreateDatabaseInCluster(srcDb, 3, srcRaft.WebUrl);

            var destNode = await CreateDatabaseInCluster(dstDb, 1, dstRaft.WebUrl);

            var node = srcNodes.Servers.First(x => x.ServerStore.NodeTag != srcRaft.ServerStore.NodeTag).ServerStore.NodeTag;

            using (var src = new DocumentStore
            {
                Urls = srcNodes.Servers.Select(s => s.WebUrl).ToArray(),
                Database = srcDb,
            }.Initialize())
                using (var dest = new DocumentStore
                {
                    Urls = new[] { destNode.Servers[0].WebUrl },
                    Database = dstDb,
                }.Initialize())
                {
                    var connectionStringName = "EtlFailover";
                    var urls   = new[] { destNode.Servers[0].WebUrl };
                    var config = new RavenEtlConfiguration()
                    {
                        Name = connectionStringName,
                        ConnectionStringName = connectionStringName,
                        Transforms           =
                        {
                            new Transformation
                            {
                                Name                = $"ETL : {connectionStringName}",
                                Collections         = new List <string>(new[] { "Users" }),
                                Script              = null,
                                ApplyToAllDocuments = false,
                                Disabled            = false
                            }
                        },
                        LoadRequestTimeoutInSec = 30,
                        MentorNode = node
                    };
                    var connectionString = new RavenConnectionString
                    {
                        Name     = connectionStringName,
                        Database = dest.Database,
                        TopologyDiscoveryUrls = urls,
                    };

                    var result = src.Maintenance.Send(new PutConnectionStringOperation <RavenConnectionString>(connectionString));
                    Assert.NotNull(result.RaftCommandIndex);

                    src.Maintenance.Send(new AddEtlOperation <RavenConnectionString>(config));
                    var originalTaskNode = srcNodes.Servers.Single(s => s.ServerStore.NodeTag == node);

                    using (var session = src.OpenSession())
                    {
                        session.Store(new User()
                        {
                            Name = "Joe Doe"
                        }, "users/1");

                        session.SaveChanges();
                    }

                    Assert.True(WaitForDocument <User>(dest, "users/1", u => u.Name == "Joe Doe", 30_000));
                    await ActionWithLeader((l) => l.ServerStore.RemoveFromClusterAsync(node));

                    await originalTaskNode.ServerStore.WaitForState(RachisState.Passive, CancellationToken.None);

                    using (var session = src.OpenSession())
                    {
                        session.Store(new User()
                        {
                            Name = "Joe Doe2"
                        }, "users/2");

                        session.SaveChanges();
                    }

                    Assert.True(WaitForDocument <User>(dest, "users/2", u => u.Name == "Joe Doe2", 30_000));
                    Assert.Throws <NodeIsPassiveException>(() =>
                    {
                        using (var originalSrc = new DocumentStore
                        {
                            Urls = new[] { originalTaskNode.WebUrl },
                            Database = srcDb,
                            Conventions = new DocumentConventions
                            {
                                DisableTopologyUpdates = true
                            }
                        }.Initialize())
                        {
                            using (var session = originalSrc.OpenSession())
                            {
                                session.Store(new User()
                                {
                                    Name = "Joe Doe3"
                                }, "users/3");

                                session.SaveChanges();
                            }
                        }
                    });
                }
        }
Example #24
0
        public async Task AutoNamingAlgorithmOfOngoingTasksShouldTakeNameAlreadyExistsIntoAccount()
        {
            using (var store = GetDocumentStore())
            {
                var dbName = $"db/{Guid.NewGuid()}";
                var csName = $"cs/{Guid.NewGuid()}";

                var connectionString = new RavenConnectionString
                {
                    Name     = csName,
                    Database = dbName,
                    TopologyDiscoveryUrls = new[] { "http://127.0.0.1:12345" }
                };

                var result = await store.Maintenance.SendAsync(new PutConnectionStringOperation <RavenConnectionString>(connectionString));

                Assert.NotNull(result.RaftCommandIndex);

                await store.Maintenance.SendAsync(new UpdateExternalReplicationOperation(new ExternalReplication(dbName, csName)));

                await store.Maintenance.SendAsync(new UpdateExternalReplicationOperation(new ExternalReplication(dbName, csName)));

                var backupConfig = Backup.CreateBackupConfiguration(backupPath: NewDataPath(suffix: "BackupFolder"), fullBackupFrequency: "* */1 * * *", incrementalBackupFrequency: "* */2 * * *", azureSettings: new AzureSettings
                {
                    StorageContainer = "abc"
                }, disabled: true);

                await store.Maintenance.SendAsync(new UpdatePeriodicBackupOperation(backupConfig));

                await store.Maintenance.SendAsync(new UpdatePeriodicBackupOperation(backupConfig));

                var etlConfiguration = new RavenEtlConfiguration
                {
                    ConnectionStringName = csName,
                    Transforms           =
                    {
                        new Transformation()
                        {
                            Name        = "loadAll",
                            Collections ={ "Users"                   },
                            Script      = "loadToUsers(this)"
                        }
                    }
                };

                await store.Maintenance.SendAsync(new AddEtlOperation <RavenConnectionString>(etlConfiguration));

                await store.Maintenance.SendAsync(new AddEtlOperation <RavenConnectionString>(etlConfiguration));


                // for Pull Replication Hub name is required - no need to test

                var sink = new PullReplicationAsSink
                {
                    HubDefinitionName    = "aa",
                    ConnectionString     = connectionString,
                    ConnectionStringName = connectionString.Name
                };

                await store.Maintenance.SendAsync(new UpdatePullReplicationAsSinkOperation(sink));

                await store.Maintenance.SendAsync(new UpdatePullReplicationAsSinkOperation(sink));
            }
        }
Example #25
0
        public async Task ShouldSendCounterChangeMadeInCluster()
        {
            var srcDb = "13288-src";
            var dstDb = "13288-dst";

            var(_, srcRaft) = await CreateRaftCluster(2);

            var(_, dstRaft) = await CreateRaftCluster(1);

            var srcNodes = await CreateDatabaseInCluster(srcDb, 2, srcRaft.WebUrl);

            var destNode = await CreateDatabaseInCluster(dstDb, 1, dstRaft.WebUrl);

            using (var src = new DocumentStore
            {
                Urls = srcNodes.Servers.Select(s => s.WebUrl).ToArray(),
                Database = srcDb,
            }.Initialize())
                using (var dest = new DocumentStore
                {
                    Urls = new[] { destNode.Servers[0].WebUrl },
                    Database = dstDb,
                }.Initialize())
                {
                    var connectionStringName = "my-etl";
                    var urls   = new[] { destNode.Servers[0].WebUrl };
                    var config = new RavenEtlConfiguration()
                    {
                        Name = connectionStringName,
                        ConnectionStringName = connectionStringName,
                        Transforms           =
                        {
                            new Transformation
                            {
                                Name                = $"ETL : {connectionStringName}",
                                Collections         = new List <string>(new[] { "Users" }),
                                Script              = null,
                                ApplyToAllDocuments = false,
                                Disabled            = false
                            }
                        },
                        LoadRequestTimeoutInSec = 30,
                        MentorNode = "A"
                    };
                    var connectionString = new RavenConnectionString
                    {
                        Name     = connectionStringName,
                        Database = dest.Database,
                        TopologyDiscoveryUrls = urls,
                    };

                    var result = src.Maintenance.Send(new PutConnectionStringOperation <RavenConnectionString>(connectionString));
                    Assert.NotNull(result.RaftCommandIndex);

                    src.Maintenance.Send(new AddEtlOperation <RavenConnectionString>(config));

                    var aNode = srcNodes.Servers.Single(s => s.ServerStore.NodeTag == "A");
                    var bNode = srcNodes.Servers.Single(s => s.ServerStore.NodeTag == "B");

                    // modify counter on A node (mentor of ETL task)

                    using (var aSrc = new DocumentStore
                    {
                        Urls = new[] { aNode.WebUrl },
                        Database = srcDb,
                        Conventions = new DocumentConventions
                        {
                            DisableTopologyUpdates = true
                        }
                    }.Initialize())
                    {
                        using (var session = aSrc.OpenSession())
                        {
                            session.Store(new User()
                            {
                                Name = "Joe Doe"
                            }, "users/1");

                            session.CountersFor("users/1").Increment("likes");

                            session.Advanced.WaitForReplicationAfterSaveChanges();

                            session.SaveChanges();
                        }
                    }

                    Assert.True(WaitForDocument <User>(dest, "users/1", u => u.Name == "Joe Doe", 30_000));

                    using (var session = dest.OpenSession())
                    {
                        var user = session.Load <User>("users/1");

                        Assert.NotNull(user);
                        Assert.Equal("Joe Doe", user.Name);

                        var counter = session.CountersFor("users/1").Get("likes");

                        Assert.NotNull(counter);
                        Assert.Equal(1, counter.Value);
                    }

                    // modify counter on B node (not mentor)

                    using (var bSrc = new DocumentStore
                    {
                        Urls = new[] { bNode.WebUrl },
                        Database = srcDb,
                        Conventions = new DocumentConventions
                        {
                            DisableTopologyUpdates = true
                        }
                    }.Initialize())
                    {
                        using (var session = bSrc.OpenSession())
                        {
                            session.CountersFor("users/1").Increment("likes");

                            session.SaveChanges();
                        }
                    }

                    Assert.True(Replication.WaitForCounterReplication(new List <IDocumentStore>
                    {
                        dest
                    }, "users/1", "likes", 2, TimeSpan.FromSeconds(60)));
                }
        }
Example #26
0
        public void HandleDatabaseRecordChange(DatabaseRecord record)
        {
            if (record == null)
            {
                return;
            }

            var myRavenEtl = new List <RavenEtlConfiguration>();
            var mySqlEtl   = new List <SqlEtlConfiguration>();

            foreach (var config in record.RavenEtls)
            {
                if (IsMyEtlTask <RavenEtlConfiguration, RavenConnectionString>(record, config))
                {
                    myRavenEtl.Add(config);
                }
            }

            foreach (var config in record.SqlEtls)
            {
                if (IsMyEtlTask <SqlEtlConfiguration, SqlConnectionString>(record, config))
                {
                    mySqlEtl.Add(config);
                }
            }

            var toRemove = _processes.GroupBy(x => x.ConfigurationName).ToDictionary(x => x.Key, x => x.ToList());

            foreach (var processesPerConfig in _processes.GroupBy(x => x.ConfigurationName))
            {
                var process = processesPerConfig.First();

                Debug.Assert(processesPerConfig.All(x => x.GetType() == process.GetType()));

                if (process is RavenEtl ravenEtl)
                {
                    RavenEtlConfiguration existing = null;
                    foreach (var config in myRavenEtl)
                    {
                        if (ravenEtl.Configuration.IsEqual(config))
                        {
                            existing = config;
                            break;
                        }
                    }
                    if (existing != null)
                    {
                        toRemove.Remove(processesPerConfig.Key);
                        myRavenEtl.Remove(existing);
                    }
                }
                else if (process is SqlEtl sqlEtl)
                {
                    SqlEtlConfiguration existing = null;
                    foreach (var config in mySqlEtl)
                    {
                        if (sqlEtl.Configuration.IsEqual(config))
                        {
                            existing = config;
                            break;
                        }
                    }
                    if (existing != null)
                    {
                        toRemove.Remove(processesPerConfig.Key);
                        mySqlEtl.Remove(existing);
                    }
                }
                else
                {
                    throw new InvalidOperationException($"Unknown ETL process type: {process.GetType()}");
                }
            }

            Parallel.ForEach(toRemove, x =>
            {
                foreach (var process in x.Value)
                {
                    try
                    {
                        process.Stop();
                    }
                    catch (Exception e)
                    {
                        if (Logger.IsInfoEnabled)
                        {
                            Logger.Info($"Failed to stop ETL process {process.Name} on the database record change", e);
                        }
                    }
                }
            });

            LoadProcesses(record, myRavenEtl, mySqlEtl, toRemove.SelectMany(x => x.Value).ToList());

            // unsubscribe old etls _after_ we start new processes to ensure the tombstone cleaner
            // constantly keeps track of tombstones processed by ETLs so it won't delete them during etl processes reloading

            foreach (var processesPerConfig in toRemove)
            {
                foreach (var process in processesPerConfig.Value)
                {
                    _database.TombstoneCleaner.Unsubscribe(process);
                }
            }

            Parallel.ForEach(toRemove, x =>
            {
                foreach (var process in x.Value)
                {
                    try
                    {
                        process.Dispose();
                    }
                    catch (Exception e)
                    {
                        if (Logger.IsInfoEnabled)
                        {
                            Logger.Info($"Failed to dispose ETL process {process.Name} on the database record change", e);
                        }
                    }
                }
            });
        }
Example #27
0
        public async Task CanGetTaskInfo()
        {
            var clusterSize  = 3;
            var databaseName = "TestDB";
            var leader       = await CreateRaftClusterAndGetLeader(clusterSize);

            ModifyOngoingTaskResult             addWatcherRes;
            UpdatePeriodicBackupOperationResult updateBackupResult;
            AddEtlOperationResult addRavenEtlResult;
            AddEtlOperationResult addSqlEtlResult;
            RavenEtlConfiguration etlConfiguration;
            SqlEtlConfiguration   sqlConfiguration;
            ExternalReplication   watcher;
            SqlConnectionString   sqlConnectionString;

            var sqlScript = @"
var orderData = {
    Id: __document_id,
    OrderLinesCount: this.OrderLines.length,
    TotalCost: 0
};

loadToOrders(orderData);
";

            using (var store = new DocumentStore
            {
                Urls = new[] { leader.WebUrl },
                Database = databaseName
            }.Initialize())
            {
                var doc            = new DatabaseRecord(databaseName);
                var databaseResult = await store.Admin.Server.SendAsync(new CreateDatabaseOperation(doc, clusterSize));

                Assert.Equal(clusterSize, databaseResult.Topology.AllNodes.Count());
                foreach (var server in Servers)
                {
                    await server.ServerStore.Cluster.WaitForIndexNotification(databaseResult.RaftCommandIndex);
                }
                foreach (var server in Servers)
                {
                    await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(databaseName);
                }

                watcher = new ExternalReplication
                {
                    Database = "Watcher1",
                    Url      = "http://127.0.0.1:9090",
                    Name     = "MyExternalReplication"
                };

                addWatcherRes = await AddWatcherToReplicationTopology((DocumentStore)store, watcher);

                var backupConfig = new PeriodicBackupConfiguration
                {
                    Name          = "backup1",
                    LocalSettings = new LocalSettings
                    {
                        FolderPath = NewDataPath(suffix: "BackupFolder")
                    },
                    AzureSettings = new AzureSettings
                    {
                        StorageContainer = "abc"
                    },
                    FullBackupFrequency        = "* */1 * * *",
                    IncrementalBackupFrequency = "* */2 * * *",
                    Disabled = true
                };

                updateBackupResult = await store.Admin.Server.SendAsync(new UpdatePeriodicBackupOperation(backupConfig, store.Database));

                store.Admin.Server.Send(new PutConnectionStringOperation <RavenConnectionString>(new RavenConnectionString
                {
                    Name     = "cs",
                    Url      = "http://127.0.0.1:8080",
                    Database = "Northwind",
                }, store.Database));

                etlConfiguration = new RavenEtlConfiguration()
                {
                    Name = "tesst",
                    ConnectionStringName = "cs",
                    Transforms           =
                    {
                        new Transformation()
                        {
                            Name        = "loadAll",
                            Collections ={ "Users"                   },
                            Script      = "loadToUsers(this)"
                        }
                    }
                };

                addRavenEtlResult = store.Admin.Server.Send(new AddEtlOperation <RavenConnectionString>(etlConfiguration, store.Database));

                sqlConnectionString = new SqlConnectionString
                {
                    Name             = "abc",
                    ConnectionString = @"Data Source=localhost\sqlexpress;Integrated Security=SSPI;Connection Timeout=3" + $";Initial Catalog=SqlReplication-{store.Database};"
                };
                store.Admin.Server.Send(new PutConnectionStringOperation <SqlConnectionString>(sqlConnectionString, store.Database));


                sqlConfiguration = new SqlEtlConfiguration()
                {
                    Name = "abc",
                    ConnectionStringName = "abc",
                    FactoryName          = "System.Data.SqlClient",
                    SqlTables            =
                    {
                        new SqlEtlTable {
                            TableName = "Orders", DocumentIdColumn = "Id", InsertOnlyMode = false
                        },
                        new SqlEtlTable {
                            TableName = "OrderLines", DocumentIdColumn = "OrderId", InsertOnlyMode = false
                        },
                    },
                    Transforms =
                    {
                        new Transformation()
                        {
                            Name        = "OrdersAndLines",
                            Collections = new List <string>{
                                "Orders"
                            },
                            Script = sqlScript
                        }
                    }
                };
                addSqlEtlResult = store.Admin.Server.Send(new AddEtlOperation <SqlConnectionString>(sqlConfiguration, store.Database));
            }

            using (var store = new DocumentStore
            {
                Urls = new[] { leader.WebUrl },
                Database = databaseName,
                Conventions =
                {
                    DisableTopologyUpdates = true
                }
            }.Initialize())
            {
                var taskId            = addWatcherRes.TaskId;
                var replicationResult = (OngoingTaskReplication) await GetTaskInfo((DocumentStore)store, taskId, OngoingTaskType.Replication);

                Assert.Equal(watcher.Database, replicationResult.DestinationDatabase);
                Assert.Equal(watcher.Url, replicationResult.DestinationUrl);
                Assert.Equal(watcher.Name, replicationResult.TaskName);

                taskId = updateBackupResult.TaskId;
                var backupResult = (OngoingTaskBackup) await GetTaskInfo((DocumentStore)store, taskId, OngoingTaskType.Backup);

                Assert.Equal("Local", backupResult.BackupDestinations[0]);
                Assert.Equal("Azure", backupResult.BackupDestinations[1]);
                Assert.Equal("backup1", backupResult.TaskName);
                Assert.Equal(OngoingTaskState.Disabled, backupResult.TaskState);

                taskId = addRavenEtlResult.TaskId;

                var etlResult = (OngoingTaskRavenEtl) await GetTaskInfo((DocumentStore)store, taskId, OngoingTaskType.RavenEtl);

                Assert.Equal("cs", etlResult.Configuration.ConnectionStringName);
                Assert.Equal("tesst", etlResult.Configuration.Name);
                Assert.Equal("loadAll", etlResult.Configuration.Transforms[0].Name);
                Assert.Equal("loadToUsers(this)", etlResult.Configuration.Transforms[0].Script);
                Assert.Equal("Users", etlResult.Configuration.Transforms[0].Collections[0]);
                Assert.Equal(etlConfiguration.Name, etlResult?.TaskName);

                taskId = addSqlEtlResult.TaskId;

                var sqlResult = (OngoingTaskSqlEtl) await GetTaskInfo((DocumentStore)store, taskId, OngoingTaskType.SqlEtl);

                Assert.Equal("abc", sqlResult.Configuration.ConnectionStringName);
                Assert.Equal("abc", sqlResult.Configuration.Name);
                Assert.Equal("OrdersAndLines", sqlResult.Configuration.Transforms[0].Name);
                Assert.Equal(sqlScript, sqlResult.Configuration.Transforms[0].Script);
                Assert.Equal("Orders", sqlResult.Configuration.Transforms[0].Collections[0]);
                Assert.NotNull(sqlResult.Configuration.SqlTables);
                Assert.Equal(sqlConfiguration.Name, sqlResult?.TaskName);
            }
        }
Example #28
0
        public async Task WillWorkAfterResponsibleNodeRestart_RavenDB_13237()
        {
            var srcDb = "ETL-src";
            var dstDb = "ETL-dst";

            var(_, srcRaft) = await CreateRaftCluster(3, shouldRunInMemory : false);

            var(_, dstRaft) = await CreateRaftCluster(1);

            var srcNodes = await CreateDatabaseInCluster(srcDb, 2, srcRaft.WebUrl);

            var destNode = await CreateDatabaseInCluster(dstDb, 1, dstRaft.WebUrl);

            using (var src = new DocumentStore
            {
                Urls = srcNodes.Servers.Select(s => s.WebUrl).ToArray(),
                Database = srcDb,
            }.Initialize())
                using (var dest = new DocumentStore
                {
                    Urls = new[] { destNode.Servers[0].WebUrl },
                    Database = dstDb,
                }.Initialize())
                {
                    var name   = "FailoverAfterRestart";
                    var urls   = new[] { destNode.Servers[0].WebUrl };
                    var config = new RavenEtlConfiguration()
                    {
                        Name = name,
                        ConnectionStringName = name,
                        Transforms           =
                        {
                            new Transformation
                            {
                                Name                = $"ETL : {name}",
                                Collections         = new List <string>(new[] { "Users" }),
                                Script              = null,
                                ApplyToAllDocuments = false,
                                Disabled            = false
                            }
                        },
                        LoadRequestTimeoutInSec = 30,
                    };
                    var connectionString = new RavenConnectionString
                    {
                        Name     = name,
                        Database = dest.Database,
                        TopologyDiscoveryUrls = urls,
                    };

                    var result = src.Maintenance.Send(new PutConnectionStringOperation <RavenConnectionString>(connectionString));
                    Assert.NotNull(result.RaftCommandIndex);

                    src.Maintenance.Send(new AddEtlOperation <RavenConnectionString>(config));

                    var ongoingTask = src.Maintenance.Send(new GetOngoingTaskInfoOperation(name, OngoingTaskType.RavenEtl));

                    var responsibleNodeNodeTag = ongoingTask.ResponsibleNode.NodeTag;
                    var originalTaskNodeServer = srcNodes.Servers.Single(s => s.ServerStore.NodeTag == responsibleNodeNodeTag);

                    using (var session = src.OpenSession())
                    {
                        session.Store(new User()
                        {
                            Name = "Joe Doe"
                        }, "users/1");

                        session.SaveChanges();
                    }

                    Assert.True(WaitForDocument <User>(dest, "users/1", u => u.Name == "Joe Doe", 30_000));

                    var originalResult = DisposeServerAndWaitForFinishOfDisposal(originalTaskNodeServer);

                    using (var session = src.OpenSession())
                    {
                        session.Store(new User()
                        {
                            Name = "Joe Doe2"
                        }, "users/2");

                        session.SaveChanges();
                    }

                    Assert.True(WaitForDocument <User>(dest, "users/2", u => u.Name == "Joe Doe2", 30_000));

                    ongoingTask = src.Maintenance.Send(new GetOngoingTaskInfoOperation(name, OngoingTaskType.RavenEtl));

                    var currentNodeNodeTag    = ongoingTask.ResponsibleNode.NodeTag;
                    var currentTaskNodeServer = srcNodes.Servers.Single(s => s.ServerStore.NodeTag == currentNodeNodeTag);

                    // start server which originally was handling ETL task
                    GetNewServer(new ServerCreationOptions
                    {
                        CustomSettings = new Dictionary <string, string>
                        {
                            { RavenConfiguration.GetKey(x => x.Core.ServerUrls), originalResult.Url }
                        },
                        RunInMemory    = false,
                        DeletePrevious = false,
                        DataDirectory  = originalResult.DataDirectory
                    });

                    using (var store = new DocumentStore
                    {
                        Urls = new[] { originalResult.Url },
                        Database = srcDb,
                        Conventions =
                        {
                            DisableTopologyUpdates = true
                        }
                    }.Initialize())
                    {
                        using (var session = store.OpenSession())
                        {
                            session.Store(new User()
                            {
                                Name = "Joe Doe3"
                            }, "users/3");

                            session.SaveChanges();
                        }

                        Assert.True(WaitForDocument <User>(dest, "users/3", u => u.Name == "Joe Doe3", 30_000));

                        // force disposing second node to ensure the original node is reponsible for ETL task again
                        DisposeServerAndWaitForFinishOfDisposal(currentTaskNodeServer);

                        using (var session = store.OpenSession())
                        {
                            session.Store(new User()
                            {
                                Name = "Joe Doe4"
                            }, "users/4");

                            session.SaveChanges();
                        }

                        Assert.True(WaitForDocument <User>(dest, "users/4", u => u.Name == "Joe Doe4", 30_000));
                    }
                }
        }
Example #29
0
        public async Task EtlTombstonesInTheCluster()
        {
            var cluster = await CreateRaftCluster(3);

            using (var store = GetDocumentStore(new Options
            {
                Server = cluster.Leader,
                ModifyDocumentStore = s => s.Conventions = new DocumentConventions(),
                ReplicationFactor = 3
            }))
                using (var dest = GetDocumentStore())
                {
                    var connectionString = await store.Maintenance.SendAsync(new PutConnectionStringOperation <RavenConnectionString>(new RavenConnectionString
                    {
                        Name = "test",
                        TopologyDiscoveryUrls = dest.Urls,
                        Database = dest.Database,
                    }));

                    Assert.NotNull(connectionString.RaftCommandIndex);

                    var configuration = new RavenEtlConfiguration()
                    {
                        ConnectionStringName = "test",
                        Name       = "myConfiguration",
                        MentorNode = "A",
                        Transforms = new List <Transformation>
                        {
                            new Transformation
                            {
                                ApplyToAllDocuments = true,
                                Name = "blah"
                            }
                        }
                    };

                    await store.Maintenance.SendAsync(new AddEtlOperation <RavenConnectionString>(configuration));

                    var etlStorage = await cluster.Nodes.Single(n => n.ServerStore.NodeTag == "A").ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database);

                    var mre  = new ManualResetEventSlim(false);
                    var sent = new ManualResetEventSlim(false);
                    etlStorage.EtlLoader.BatchCompleted += _ =>
                    {
                        sent.Set();
                        mre.Wait(TimeSpan.FromSeconds(30));
                    };

                    using (var session = store.OpenSession())
                    {
                        session.Advanced.WaitForReplicationAfterSaveChanges(replicas: 2);
                        session.Store(new User {
                            Name = "Karmel"
                        }, "foo/bar");
                        session.SaveChanges();
                    }

                    if (sent.Wait(TimeSpan.FromSeconds(30)) == false)
                    {
                        Assert.False(true, "timeout!");
                    }

                    using (var session = store.OpenSession())
                    {
                        session.Advanced.WaitForReplicationAfterSaveChanges(replicas: 2);
                        session.Delete("foo/bar");
                        session.SaveChanges();
                    }

                    using (var session = store.OpenSession())
                    {
                        session.Advanced.WaitForReplicationAfterSaveChanges(replicas: 2);
                        session.Store(new User {
                            Name = "Karmel"
                        }, "marker");
                        session.SaveChanges();

                        Assert.True(await WaitForDocumentInClusterAsync <User>((DocumentSession)session, "marker", (u) => u.Id == "marker", TimeSpan.FromSeconds(15)));
                    }

                    var total = 0L;
                    foreach (var server in cluster.Nodes)
                    {
                        var storage = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database);

                        using (storage.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                            using (context.OpenReadTransaction())
                            {
                                total += storage.DocumentsStorage.GetNumberOfTombstones(context);
                            }
                    }
                    Assert.Equal(3, total);

                    total = 0L;
                    foreach (var server in cluster.Nodes)
                    {
                        var storage = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database);

                        await storage.TombstoneCleaner.ExecuteCleanup();

                        using (storage.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                            using (context.OpenReadTransaction())
                            {
                                total += storage.DocumentsStorage.GetNumberOfTombstones(context);
                            }
                    }
                    Assert.Equal(3, total); // we didn't send the tombstone so we must not purge it

                    await WaitForLastReplicationEtag(cluster, store);

                    await DisposeServerAndWaitForFinishOfDisposalAsync(etlStorage.ServerStore.Server);

                    using (var session = store.OpenSession())
                    {
                        session.Store(new User {
                            Name = "Karmel"
                        }, "marker2");
                        session.SaveChanges();
                    }

                    WaitForDocument(dest, "marker2");
                    string changeVectorMarker2;

                    using (var session = dest.OpenSession())
                    {
                        Assert.Null(session.Load <User>("foo/bar"));
                    }
                    using (var session = store.OpenSession())
                    {
                        var marker = session.Load <User>("marker2");
                        changeVectorMarker2 = session.Advanced.GetChangeVectorFor(marker);
                    }

                    await ActionWithLeader((l) => WaitForRaftCommandToBeAppliedInCluster(l, nameof(UpdateEtlProcessStateCommand)));

                    Assert.True(await WaitForEtlState(cluster, store, changeVectorMarker2));

                    total = 0;
                    foreach (var server in cluster.Nodes)
                    {
                        if (server.Disposed)
                        {
                            continue;
                        }
                        var storage = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store.Database);

                        await storage.TombstoneCleaner.ExecuteCleanup();

                        using (storage.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                            using (context.OpenReadTransaction())
                            {
                                total += storage.DocumentsStorage.GetNumberOfTombstones(context);
                            }
                    }
                    Assert.Equal(0, total);
                }
        }
Example #30
0
        public void HandleDatabaseRecordChange(DatabaseRecord record)
        {
            if (record == null)
            {
                return;
            }

            var myRavenEtl = new List <RavenEtlConfiguration>();
            var mySqlEtl   = new List <SqlEtlConfiguration>();

            foreach (var config in record.RavenEtls)
            {
                if (IsMyEtlTask <RavenEtlConfiguration, RavenConnectionString>(record, config))
                {
                    myRavenEtl.Add(config);
                }
            }

            foreach (var config in record.SqlEtls)
            {
                if (IsMyEtlTask <SqlEtlConfiguration, SqlConnectionString>(record, config))
                {
                    mySqlEtl.Add(config);
                }
            }

            var toRemove = new List <EtlProcess>(_processes);

            foreach (var etlProcess in _processes)
            {
                if (etlProcess is RavenEtl ravenEtl)
                {
                    RavenEtlConfiguration existing = null;
                    foreach (var config in myRavenEtl)
                    {
                        if (ravenEtl.Configuration.IsEqual(config))
                        {
                            existing = config;
                            break;
                        }
                    }
                    if (existing != null)
                    {
                        toRemove.Remove(etlProcess);
                        myRavenEtl.Remove(existing);
                    }
                }

                if (etlProcess is SqlEtl sqlEtl)
                {
                    SqlEtlConfiguration existing = null;
                    foreach (var config in mySqlEtl)
                    {
                        if (sqlEtl.Configuration.IsEqual(config))
                        {
                            existing = config;
                            break;
                        }
                    }
                    if (existing != null)
                    {
                        toRemove.Remove(etlProcess);
                        mySqlEtl.Remove(existing);
                    }
                }
            }

            Parallel.ForEach(toRemove, x =>
            {
                try
                {
                    x.Stop();
                }
                catch (Exception e)
                {
                    if (Logger.IsInfoEnabled)
                    {
                        Logger.Info($"Failed to stop ETL process {x.Name} on the database record change", e);
                    }
                }
            });

            LoadProcesses(record, myRavenEtl, mySqlEtl, toRemove);

            // unsubscribe old etls _after_ we start new processes to ensure the tombstone cleaner
            // constantly keeps track of tombstones processed by ETLs so it won't delete them during etl processes reloading

            foreach (var process in toRemove)
            {
                _database.TombstoneCleaner.Unsubscribe(process);
            }

            Parallel.ForEach(toRemove, x =>
            {
                try
                {
                    x.Dispose();
                }
                catch (Exception e)
                {
                    if (Logger.IsInfoEnabled)
                    {
                        Logger.Info($"Failed to dispose ETL process {x.Name} on the database record change", e);
                    }
                }
            });
        }