public async Task RunBackup() { var localFolder = Path.Combine(Directory.GetCurrentDirectory(), "raven-bkp"); if (!Directory.Exists(localFolder)) { Directory.CreateDirectory(localFolder); } var config = new PeriodicBackupConfiguration { Name = "Backup", BackupType = BackupType.Backup, FullBackupFrequency = "*/10 * * * *", IncrementalBackupFrequency = "0 2 * * *", LocalSettings = new LocalSettings { FolderPath = localFolder } }; var operation = new UpdatePeriodicBackupOperation(config); var result = await Context.Database.Store.Maintenance.SendAsync(operation); await Context.Database.Store.Maintenance.SendAsync(new StartBackupOperation(true, result.TaskId)); await ReplyAsync($"Database backed up to {Path.Combine(Directory.GetCurrentDirectory(), "raven-bkp")}"); }
public async Task can_backup_to_directory() { var backupPath = NewDataPath(suffix: "BackupFolder"); using (var store = GetDocumentStore()) { using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "oren" }, "users/1"); await session.SaveChangesAsync(); } var config = new PeriodicBackupConfiguration { LocalSettings = new LocalSettings { FolderPath = backupPath }, IncrementalBackupFrequency = "* * * * *" //every minute }; var operation = new UpdatePeriodicBackupOperation(config); var result = await store.Maintenance.SendAsync(operation); var periodicBackupTaskId = result.TaskId; var getPeriodicBackupStatus = new GetPeriodicBackupStatusOperation(periodicBackupTaskId); var done = SpinWait.SpinUntil(() => store.Maintenance.Send(getPeriodicBackupStatus).Status?.LastFullBackup != null, TimeSpan.FromSeconds(180)); Assert.True(done, "Failed to complete the backup in time"); } using (var store = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_2" })) { await store.Smuggler.ImportIncrementalAsync(new DatabaseSmugglerImportOptions(), Directory.GetDirectories(backupPath).First()); using (var session = store.OpenAsyncSession()) { var user = await session.LoadAsync <User>("users/1"); Assert.NotNull(user); Assert.Equal("oren", user.Name); } } }
static async Task MainInternal() { #region encrypted_database // path to the certificate you received during the server setup var cert = new X509Certificate2(@"C:\Users\RavenDB\authentication_key\admin.client.certificate.RavenDBdom.pfx"); using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { // Backup & Restore procedures here } #endregion using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { var config = new PeriodicBackupConfiguration { LocalSettings = new LocalSettings { //Backup files local path FolderPath = @"E:\RavenBackups" }, //Full Backup period (Cron expression for a 3-hours period) FullBackupFrequency = "0 */3 * * *", //Type can be Backup or Snapshot BackupType = BackupType.Backup, //Task Name Name = "fullBackupTask", BackupEncryptionSettings = new BackupEncryptionSettings { #region use_database_encryption_key //Use the same encryption key as the database EncryptionMode = EncryptionMode.UseDatabaseKey #endregion } }; var operation = new UpdatePeriodicBackupOperation(config); var result = await docStore.Maintenance.SendAsync(operation); } using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { #region use_database_encryption_key_full_sample var config = new PeriodicBackupConfiguration { //additional settings here.. //.. //This is a logical-backup BackupType = BackupType.Backup, BackupEncryptionSettings = new BackupEncryptionSettings { //Use the same encryption key as the database EncryptionMode = EncryptionMode.UseDatabaseKey } }; var operation = new UpdatePeriodicBackupOperation(config); var result = await docStore.Maintenance.SendAsync(operation); #endregion } using (var docStore = new DocumentStore) { var config = new PeriodicBackupConfiguration { LocalSettings = new LocalSettings { //Backup files local path FolderPath = @"E:\RavenBackups" }, //Full Backup period (Cron expression for a 3-hours period) FullBackupFrequency = "0 */3 * * *", //Type can be Backup or Snapshot BackupType = BackupType.Backup, //Task Name Name = "fullBackupTask", BackupEncryptionSettings = new BackupEncryptionSettings { #region use_provided_encryption_key //Use an encryption key of your choice EncryptionMode = EncryptionMode.UseProvidedKey, Key = "OI7Vll7DroXdUORtc6Uo64wdAk1W0Db9ExXXgcg5IUs=" #endregion } }; var operation = new UpdatePeriodicBackupOperation(config); var result = await docStore.Maintenance.SendAsync(operation); } using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { var config = new PeriodicBackupConfiguration { // additional settings here.. //.. #region use_provided_encryption_key_full_sample BackupEncryptionSettings = new BackupEncryptionSettings { //Use an encryption key of your choice EncryptionMode = EncryptionMode.UseProvidedKey, Key = "OI7Vll7DroXdUORtc6Uo64wdAk1W0Db9ExXXgcg5IUs=" } #endregion }; var operation = new UpdatePeriodicBackupOperation(config); var result = await docStore.Maintenance.SendAsync(operation); } using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { var config = new PeriodicBackupConfiguration { // additional settings here.. //.. #region explicitly_choose_no_encryption BackupEncryptionSettings = new BackupEncryptionSettings { //No encryption EncryptionMode = EncryptionMode.None } #endregion }; var operation = new UpdatePeriodicBackupOperation(config); var result = await docStore.Maintenance.SendAsync(operation); } using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { #region restore_encrypted_backup // restore encrypted database var restoreConfiguration = new RestoreBackupConfiguration(); //New database name restoreConfiguration.DatabaseName = "newEncryptedDatabase"; //Backup-file location var backupPath = @"C:\Users\RavenDB\2019-01-06-11-11.ravendb-encryptedDatabase-A-snapshot"; restoreConfiguration.BackupLocation = backupPath; restoreConfiguration.BackupEncryptionSettings = new BackupEncryptionSettings { Key = "OI7Vll7DroXdUORtc6Uo64wdAk1W0Db9ExXXgcg5IUs=" }; var restoreBackupTask = new RestoreBackupOperation(restoreConfiguration); docStore.Maintenance.Server.Send(restoreBackupTask); #endregion } using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { // restore encrypted database var restoreConfiguration = new RestoreBackupConfiguration(); //New database name restoreConfiguration.DatabaseName = "newEncryptedDatabase"; //Backup-file location var backupPath = @"C:\Users\RavenDB\2019-01-06-11-11.ravendb-encryptedDatabase-A-snapshot"; restoreConfiguration.BackupLocation = backupPath; #region restore_encrypted_database //Restore the database using the key you encrypted it with restoreConfiguration.BackupEncryptionSettings = new BackupEncryptionSettings { Key = "OI7Vll7DroXdUORtc6Uo64wdAk1W0Db9ExXXgcg5IUs=" }; //Encrypt the restored database using this key restoreConfiguration.EncryptionKey = "1F0K2R/KkcwbkK7n4kYlv5eqisy/pMnSuJvZ2sJ/EKo="; var restoreBackupTask = new RestoreBackupOperation(restoreConfiguration); docStore.Maintenance.Server.Send(restoreBackupTask); #endregion } using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { var restoreConfiguration = new RestoreBackupConfiguration(); //New database name restoreConfiguration.DatabaseName = "newEncryptedDatabase"; //Backup-file location var backupPath = @"C:\Users\RavenDB\2019-01-06-11-11.ravendb-encryptedDatabase-A-snapshot"; restoreConfiguration.BackupLocation = backupPath; #region restore_unencrypted_database restoreConfiguration.BackupEncryptionSettings = new BackupEncryptionSettings { //No encryption EncryptionMode = EncryptionMode.None }; #endregion var restoreBackupTask = new RestoreBackupOperation(restoreConfiguration); docStore.Maintenance.Server.Send(restoreBackupTask); } using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { // restore encrypted database // restore configuration var restoreConfiguration = new RestoreBackupConfiguration(); //New database name restoreConfiguration.DatabaseName = "newEncryptedDatabase"; // restoreConfiguration.EncryptionKey = "1F0K2R/KkcwbkK7n4kYlv5eqisy/pMnSuJvZ2sJ/EKo="; #region encrypting_logical_backup_with_new_key //Restore using your own key restoreConfiguration.BackupEncryptionSettings = new BackupEncryptionSettings { Key = "OI7Vll7DroXdUORtc6Uo64wdAk1W0Db9ExXXgcg5IUs=" }; #endregion #region restore_encrypting_logical_backup_with_database_key //Restore using the DB-encryption key restoreConfiguration.EncryptionKey = "1F0K2R/KkcwbkK7n4kYlv5eqisy/pMnSuJvZ2sJ/EKo="; //Backup-file location var backupPath = @"C:\Users\RavenDB\2019-01-06-11-11.ravendb-encryptedDatabase-A-snapshot"; restoreConfiguration.BackupLocation = backupPath; var restoreBackupTask = new RestoreBackupOperation(restoreConfiguration); docStore.Maintenance.Server.Send(restoreBackupTask); #endregion }
static async Task MainInternal() { using (var docStore = new DocumentStore { Urls = new[] { "http://127.0.0.1:8080" }, Database = "Products" }.Initialize()) { #region logical_full_backup_every_3_hours var config = new PeriodicBackupConfiguration { LocalSettings = new LocalSettings { //Backup files local path FolderPath = @"E:\RavenBackups" }, //Full Backup period (Cron expression for a 3-hours period) FullBackupFrequency = "0 */3 * * *", //Type can be Backup or Snapshot BackupType = BackupType.Backup, //Task Name Name = "fullBackupTask", }; var operation = new UpdatePeriodicBackupOperation(config); var result = await docStore.Maintenance.SendAsync(operation); #endregion } using (var docStore = new DocumentStore { Urls = new[] { "http://127.0.0.1:8080" }, Database = "Products" }.Initialize()) { #region encrypted_logical_full_backup var config = new PeriodicBackupConfiguration { LocalSettings = new LocalSettings { //Backup files local path FolderPath = @"E:\RavenBackups" }, //Full Backup period (Cron expression for a 3-hours period) FullBackupFrequency = "0 */3 * * *", //Type can be Backup or Snapshot BackupType = BackupType.Backup, //Task Name Name = "fullBackupTask" }; var operation = new UpdatePeriodicBackupOperation(config); var result = await docStore.Maintenance.SendAsync(operation); #endregion } using (var docStore = new DocumentStore { Urls = new[] { "http://127.0.0.1:8080" }, Database = "Products" }.Initialize()) { var config = new PeriodicBackupConfiguration { LocalSettings = new LocalSettings { FolderPath = @"E:\RavenBackups" }, #region backup_type_snapshot //Type can be Backup or Snapshot BackupType = BackupType.Snapshot, #endregion #region backup_full_backup //Full Backup period (Cron expression for a 6-hours period) FullBackupFrequency = "0 */6 * * *", #endregion #region backup_incremental_backup //Cron expression: set incremental backup period ("*/20 * * * *" is a 20-minutes period) IncrementalBackupFrequency = "*/20 * * * *", #endregion }; } using (var docStore = new DocumentStore { Urls = new[] { "http://127.0.0.1:8080" }, Database = "Products" }.Initialize()) { #region backup_remote_destinations var config = new PeriodicBackupConfiguration { LocalSettings = new LocalSettings { FolderPath = @"E:\RavenBackups" }, //FTP Backup settings FtpSettings = new FtpSettings { Url = "192.168.10.4", Port = 8080, UserName = "******", Password = "******" }, //Azure Backup settings AzureSettings = new AzureSettings { StorageContainer = "storageContainer", RemoteFolderName = "remoteFolder", AccountName = "JohnAccount", AccountKey = "key" }, //Amazon S3 bucket settings. S3Settings = new S3Settings { AwsAccessKey = "your access key here", AwsSecretKey = "your secret key here", AwsRegionName = "OPTIONAL", BucketName = "john-bucket", }, //Amazon Glacier settings. GlacierSettings = new GlacierSettings { AwsAccessKey = "your access key here", AwsSecretKey = "your secret key here", AwsRegionName = "OPTIONAL", VaultName = "john-glacier", } }; var operation = new UpdatePeriodicBackupOperation(config); var result = await docStore.Maintenance.SendAsync(operation); #endregion } using (var docStore = new DocumentStore { Urls = new[] { "http://127.0.0.1:8080" }, Database = "Products" }.Initialize()) { var config = new PeriodicBackupConfiguration { LocalSettings = new LocalSettings { FolderPath = @"E:\RavenBackups" }, //FTP Backup settings FtpSettings = new FtpSettings { Url = "192.168.10.4", Port = 8080, UserName = "******", Password = "******" }, //Azure Backup settings AzureSettings = new AzureSettings { StorageContainer = "storageContainer", RemoteFolderName = "remoteFolder", AccountName = "JohnAccount", AccountKey = "key" } }; #region initiate_immediate_backup_execution var operation = new UpdatePeriodicBackupOperation(config); var result = await docStore.Maintenance.SendAsync(operation); //run a backup task immediately await docStore.Maintenance.SendAsync(new StartBackupOperation(true, result.TaskId)); #endregion #region get_backup_execution_results //Provide GetPeriodicBackupStatusOperation with the task ID returned by RavenDB var backupStatus = new GetPeriodicBackupStatusOperation(result.TaskId); #endregion } using (var docStore = new DocumentStore { Urls = new[] { "http://127.0.0.1:8080" }, Database = "Products" }.Initialize()) { #region restore_to_single_node var restoreConfiguration = new RestoreBackupConfiguration(); //New database name restoreConfiguration.DatabaseName = "newProductsDatabase"; //Local path with a backup file var backupPath = @"C:\Users\RavenDB\backups\2018-12-26-16-17.ravendb-Products-A-backup"; restoreConfiguration.BackupLocation = backupPath; var restoreBackupTask = new RestoreBackupOperation(restoreConfiguration); docStore.Maintenance.Server.Send(restoreBackupTask); #endregion #region restore_disable_ongoing_tasks_false //Disable or Enable ongoing tasks after restoration. //Default setting is FALSE, so tasks DO run when backup is restored. restoreConfiguration.DisableOngoingTasks = false; #endregion #region restore_last_file_name_to_restore //Last incremental backup file to restore from restoreConfiguration.LastFileNameToRestore = @"2018-12-26-12-00.ravendb-incremental-backup"; #endregion #region restore_to_specific__data_directory //Restore to a pre-chosen folder var dataPath = @"C:\Users\RavenDB\backups\2018-12-26-16-17.ravendb-Products-A-backup\restoredDatabaseLocation"; restoreConfiguration.DataDirectory = dataPath; #endregion #region restore_disable_ongoing_tasks_true //Do or do not run ongoing tasks after restoration. //Default setting is FALSE, to allow tasks' execution when the backup is restored. restoreConfiguration.DisableOngoingTasks = true; #endregion } #region encrypted_database // path to the certificate you received during the server setup var cert = new X509Certificate2(@"C:\Users\RavenDB\authentication_key\admin.client.certificate.RavenDBdom.pfx"); using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { // Backup & Restore here } #endregion // path to the authentication key you received during the server setup cert = new X509Certificate2(@"C:\Users\RavenDB\authentication_key\admin.client.certificate.RavenDBdom.pfx"); using (var docStore = new DocumentStore { Urls = new[] { "https://a.RavenDBdom.development.run" }, Database = "encryptedDatabase", Certificate = cert }.Initialize()) { #region restore_encrypted_database // restore encrypted database // restore configuration var restoreConfiguration = new RestoreBackupConfiguration(); //New database name restoreConfiguration.DatabaseName = "newEncryptedDatabase"; //Backup-file location var backupPath = @"C:\Users\RavenDB\2019-01-06-11-11.ravendb-encryptedDatabase-A-snapshot"; restoreConfiguration.BackupLocation = backupPath; restoreConfiguration.EncryptionKey = "1F0K2R/KkcwbkK7n4kYlv5eqisy/pMnSuJvZ2sJ/EKo="; var restoreBackupTask = new RestoreBackupOperation(restoreConfiguration); docStore.Maintenance.Server.Send(restoreBackupTask); #endregion } }
static async Task Main(string[] args) { Console.WriteLine("wait for ravendb up"); await Task.Delay(10_000); Console.WriteLine("run"); var store = new DocumentStore() { Urls = new[] { "http://ravendb:8080" }, Database = "test" }.Initialize(); try { store.Maintenance.Server.Send(new DeleteDatabasesOperation(store.Database, true)); store.Maintenance.Server.Send(new CreateDatabaseOperation(new DatabaseRecord(store.Database))); } catch (ConcurrencyException) { // The database was already created before calling CreateDatabaseOperation } var count = store.OpenSession().Query <Employee>().Count(); var data = 100_000 - count; var lorem = await System.IO.File.ReadAllTextAsync("lorem.txt"); Console.WriteLine($"start upload {data}"); for (var i = 0; i < data; i++) { await using (var bulkInsert = store.BulkInsert()) { for (var z = 0; z < 1000; z++) { await bulkInsert.StoreAsync(new Employee { Data = lorem }); i++; } i--; } Console.WriteLine(i); } Console.WriteLine($"start backup"); var config = new PeriodicBackupConfiguration { Name = "test-backup", FullBackupFrequency = "0 */3 * * *", BackupType = BackupType.Backup, S3Settings = new S3Settings { CustomServerUrl = "http://minio:9000", AwsAccessKey = "access_key", AwsSecretKey = "secret_key", BucketName = "test", RemoteFolderName = "test" }, }; //Create a new backup task var operation = new UpdatePeriodicBackupOperation(config); var result = await store.Maintenance.SendAsync(operation); //Run the backup task immediately var o = await store.Maintenance.SendAsync(new StartBackupOperation(true, result.TaskId)); await o.WaitForCompletionAsync(); Console.WriteLine($"end backup"); Console.ReadLine(); }
public async Task periodic_backup_should_work_with_long_intervals() { var backupPath = NewDataPath(suffix: "BackupFolder"); using (var store = GetDocumentStore()) { var periodicBackupRunner = (await GetDocumentDatabaseInstanceFor(store)).PeriodicBackupRunner; // get by reflection the maxTimerTimeoutInMilliseconds field // this field is the maximum interval acceptable in .Net's threading timer // if the requested backup interval is bigger than this maximum interval, // a timer with maximum interval will be used several times until the interval cumulatively // will be equal to requested interval typeof(PeriodicBackupRunner) .GetField(nameof(PeriodicBackupRunner.MaxTimerTimeout), BindingFlags.Instance | BindingFlags.Public) .SetValue(periodicBackupRunner, TimeSpan.FromMilliseconds(100)); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "oren 1" }, "users/1"); await session.SaveChangesAsync(); } var config = new PeriodicBackupConfiguration { BackupType = BackupType.Backup, LocalSettings = new LocalSettings { FolderPath = backupPath }, IncrementalBackupFrequency = "* * * * *" //every minute }; var operation = new UpdatePeriodicBackupOperation(config, store.Database); var result = await store.Admin.Server.SendAsync(operation); var periodicBackupTaskId = result.TaskId; SpinWait.SpinUntil(() => { var getPeriodicBackupStatus = new GetPeriodicBackupStatusOperation(store.Database, periodicBackupTaskId); var status = store.Admin.Server.Send(getPeriodicBackupStatus).Status; return(status?.LastFullBackup != null); }, 2000); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "oren 2" }, "users/2"); await session.SaveChangesAsync(); } SpinWait.SpinUntil(() => { var getPeriodicBackupStatus = new GetPeriodicBackupStatusOperation(store.Database, periodicBackupTaskId); var status = store.Admin.Server.Send(getPeriodicBackupStatus).Status; return(status?.LastFullBackup != null && status.LastIncrementalBackup != null); }, TimeSpan.FromMinutes(2)); } using (var store = GetDocumentStore(new Options { ModifyDatabaseName = s => $"{s}_2" })) { await store.Smuggler.ImportIncrementalAsync(new DatabaseSmugglerImportOptions(), Directory.GetDirectories(backupPath).First()); using (var session = store.OpenAsyncSession()) { var user = await session.LoadAsync <User>("users/1"); Assert.Equal("oren 1", user.Name); user = await session.LoadAsync <User>("users/2"); Assert.Equal("oren 2", user.Name); } } }
public async Task BackupTaskShouldStayOnTheOriginalNode() { var backupPath = NewDataPath(suffix: "BackupFolder"); var cluster = await CreateRaftCluster(5); using (var store = GetDocumentStore(new Options { ReplicationFactor = 5, Server = cluster.Leader })) { using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "oren" }, "users/1"); await session.SaveChangesAsync(); Assert.True(await WaitForDocumentInClusterAsync <User>(session.Advanced.RequestExecutor.TopologyNodes, "users/1", u => u.Name == "oren", TimeSpan.FromSeconds(15))); } var config = new PeriodicBackupConfiguration { LocalSettings = new LocalSettings { FolderPath = backupPath }, IncrementalBackupFrequency = "* * * * *" //every minute }; var operation = new UpdatePeriodicBackupOperation(config); var result = await store.Maintenance.SendAsync(operation); var periodicBackupTaskId = result.TaskId; await WaitForRaftIndexToBeAppliedInCluster(periodicBackupTaskId, TimeSpan.FromSeconds(15)); await store.Maintenance.SendAsync(new StartBackupOperation(true, result.TaskId)); var getPeriodicBackupStatus = new GetPeriodicBackupStatusOperation(periodicBackupTaskId); var done = SpinWait.SpinUntil(() => store.Maintenance.Send(getPeriodicBackupStatus).Status?.LastFullBackup != null, TimeSpan.FromSeconds(180)); Assert.True(done, "Failed to complete the backup in time"); var backupInfo = new GetOngoingTaskInfoOperation(result.TaskId, OngoingTaskType.Backup); var backupInfoResult = await store.Maintenance.SendAsync(backupInfo); var originalNode = backupInfoResult.ResponsibleNode.NodeTag; var toDelete = cluster.Nodes.First(n => n.ServerStore.NodeTag != originalNode); await store.Maintenance.Server.SendAsync(new DeleteDatabasesOperation(store.Database, hardDelete : true, fromNode : toDelete.ServerStore.NodeTag, timeToWaitForConfirmation : TimeSpan.FromSeconds(30))); var nodesCount = await WaitForValueAsync(async() => { var res = await store.Maintenance.Server.SendAsync(new GetDatabaseRecordOperation(store.Database)); if (res == null) { return(-1); } return(res.Topology.Count); }, 4); Assert.Equal(4, nodesCount); backupInfoResult = await store.Maintenance.SendAsync(backupInfo); await store.Maintenance.SendAsync(new StartBackupOperation(true, backupInfoResult.TaskId)); getPeriodicBackupStatus = new GetPeriodicBackupStatusOperation(periodicBackupTaskId); done = SpinWait.SpinUntil(() => store.Maintenance.Send(getPeriodicBackupStatus).Status?.LastFullBackup != null, TimeSpan.FromSeconds(180)); Assert.True(done, "Failed to complete the backup in time"); backupInfoResult = await store.Maintenance.SendAsync(backupInfo); Assert.Equal(originalNode, backupInfoResult.ResponsibleNode.NodeTag); } }