Dans cet article, nous allons créer une publication transactionnelle, initialiser un abonné à partir d’une sauvegarde et enfin permettre l’édition de la réplication. Nativement, il n’est pas possible de modifier en totalité une publication avec l’initialisation d’un abonné par sauvegarde Cette méthode est appropriée dans les cas suivants :
Pour finir, nous verrons comment rendre cette réplication éditable car la prise en compte de nouveaux articles, entre autres, n’est pas native sur l’abonné lorsqu’il a été initialisé par sauvegarde..
NB : Dans cet exemple, nous partons du principe que le distributeur est déjà configuré. Dans tous les scripts, nous avons recours à des paramètres de template pour faciliter le remplacement de valeurs telles que le nom de la base publiée, etc.
En premier lieu, sur l’instance A, nous allons créer une BDD lambda avec une deux tables contenant chacune un jeu de données générées aléatoirement :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
CREATE DATABASE [<PublishedDatabase, SYSNAME, value>] GO USE [<PublishedDatabase, SYSNAME, value>] GO CREATE TABLE [dbo].[RandomData1]( [RowId] [int] IDENTITY(1,1) NOT NULL, [SomeInt] [int] NULL, [SomeBit] [bit] NULL, [SomeVarchar] [varchar](10) NULL, [SomeDateTime] [datetime] NULL, [SomeNumeric] [numeric](16, 2) NULL, [CreateDate] [datetime] NOT NULL CONSTRAINT [DF_RandomData1_CreateDate] DEFAULT (getdate()), [UpdateDate] [datetime] NOT NULL CONSTRAINT [DF_RandomData1_UpdateDate] DEFAULT (getdate()), CONSTRAINT [PK_RandomData1] PRIMARY KEY CLUSTERED ( [RowId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO INSERT INTO dbo.RandomData1 (SomeInt, SomeBit, SomeVarchar, SomeDateTime, SomeNumeric) SELECT TOP 10000 ABS(CHECKSUM(NEWID()))%2500+1 AS SomeId, CASE WHEN DATEPART(MILLISECOND, GETDATE()) >= 500 THEN 0 ELSE 1 END [SomeBit], CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) [SomeVarchar], DATEADD(MILLISECOND, (ABS(CHECKSUM(NEWID())) % 6000) * -1, DATEADD(MINUTE, (ABS(CHECKSUM(NEWID())) % 1000000) * -1, GETDATE())) [SomeDateTime], (ABS(CHECKSUM(NEWID())) % 100001) + ((ABS(CHECKSUM(NEWID())) % 100001) * 0.00001) [SomeNumeric] FROM Master.dbo.SysColumns t1, Master.dbo.SysColumns t2 GO CREATE TABLE [dbo].[RandomData2]( [RowId] [int] IDENTITY(1,1) NOT NULL, [SomeInt] [int] NULL, [SomeBit] [bit] NULL, [SomeVarchar] [varchar](10) NULL, [SomeDateTime] [datetime] NULL, [SomeNumeric] [numeric](16, 2) NULL, [CreateDate] [datetime] NOT NULL CONSTRAINT [DF_RandomData2_CreateDate] DEFAULT (getdate()), [UpdateDate] [datetime] NOT NULL CONSTRAINT [DF_RandomData2_UpdateDate] DEFAULT (getdate()), CONSTRAINT [PK_RandomData2] PRIMARY KEY CLUSTERED ( [RowId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO INSERT INTO dbo.RandomData2 (SomeInt, SomeBit, SomeVarchar, SomeDateTime, SomeNumeric) SELECT TOP 10000 ABS(CHECKSUM(NEWID()))%2500+1 AS SomeId, ABS(CHECKSUM(NEWID()) % 10) % 2 [SomeBit], CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) + CHAR((ABS(CHECKSUM(NEWID())) % 26) + 97) [SomeVarchar], DATEADD(MILLISECOND, (ABS(CHECKSUM(NEWID())) % 6000) * -1, DATEADD(MINUTE, (ABS(CHECKSUM(NEWID())) % 1000000) * -1, GETDATE())) [SomeDateTime], (ABS(CHECKSUM(NEWID())) % 100001) + ((ABS(CHECKSUM(NEWID())) % 100001) * 0.00001) [SomeNumeric] FROM Master.dbo.SysColumns t1, Master.dbo.SysColumns t2 GO |
Activer la publication de la BDD créée précédemment, comme suit :
1 2 3 4 5 6 7 8 |
USE [<PublishedDatabase, SYSNAME, value>] GO EXEC sp_replicationdboption @dbname = N'<PublishedDatabase, SYSNAME, value>', @optname = N'publish', @value = N'true' GO |
Créer une réplication transactionnelle sur la BDD lambda, en spécifiant le nom de la publication.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
USE [<PublishedDatabase, SYSNAME, value>] GO EXEC sp_addpublication @publication = N'<PublicationName, SYSNAME, value>', @description = N'Transactional publication of database ''<PublicationName, SYSNAME, value>'' from Publisher ''<PublicationInstance, SYSNAME, value>''.', @sync_method = N'concurrent', @retention = 0, @allow_push = N'true', @allow_pull = N'true', @allow_anonymous = N'false', @enabled_for_internet = N'false', @snapshot_in_defaultfolder = N'true', @compress_snapshot = N'false', @ftp_port = 21, @allow_subscription_copy = N'false', @add_to_active_directory = N'false', @repl_freq = N'continuous', @status = N'active', @independent_agent = N'true', @immediate_sync = N'false', @allow_sync_tran = N'false', @allow_queued_tran = N'false', @allow_dts = N'false', @replicate_ddl = 1, @allow_initialize_from_backup = N'true', @enabled_for_p2p = N'false', @enabled_for_het_sub = N'false' GO |
Notez que l’option @allow_initialize_from_backup est à true.
NB : Pour une publication déjà existante, on pourra modifier la publication comme suit après coup :
1 2 3 4 5 6 7 8 |
USE [<PublishedDatabase, SYSNAME, value>] GO EXEC sp_changepublication @publication = '<PublicationName, SYSNAME, value>', @property = 'allow_initialize_from_backup' , @value = 'true' GO |
Création d’un agent de snapshot sur la publication créée à l’étape d’avant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
USE [<PublishedDatabase, SYSNAME, value>] GO EXEC sp_addpublication_snapshot @publication = N'<PublicationName, SYSNAME, value>', @frequency_type = 1, @frequency_interval = 1, @frequency_relative_interval = 1, @frequency_recurrence_factor = 0, @frequency_subday = 8, @frequency_subday_interval = 1, @active_start_time_of_day = 0, @active_end_time_of_day = 235959, @active_start_date = 0, @active_end_date = 0, @job_login = null, @job_password = null, @publisher_security_mode = 1 GO |
Ajout d’un article dans la publication :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
USE [<PublishedDatabase, SYSNAME, value>] GO EXEC sp_addarticle @publication = N'<PublicationName, SYSNAME, value>', @article = N'RandomData1', @source_owner = N'dbo', @source_object = N'RandomData1', @type = N'logbased', @description = null, @creation_script = null, @pre_creation_cmd = N'drop', @schema_option = 0x000000000803509F, @identityrangemanagementoption = N'manual', @destination_table = N'RandomData1', @destination_owner = N'dbo', @vertical_partition = N'false', @ins_cmd = N'CALL sp_MSins_dboRandomData1', @del_cmd = N'CALL sp_MSdel_dboRandomData1', @upd_cmd = N'SCALL sp_MSupd_dboRandomData1' GO |
Sur l’instance de distribution, désactiver le job de purge des transactions de la réplication. Le nom du job varie suivant le nom de la base de distribution utilisée par la publication.
1 2 |
EXEC msdb..sp_update_job @job_name = 'Distribution clean up: distribution', @enabled = 0; GO |
Une fois la réplication configurée, lancer une sauvegarde de la base publiée :
1 2 3 |
BACKUP DATABASE [<PublishedDatabase, SYSNAME, value>] TO DISK = N'<BackupFullPath, VARCHAR(500), value>' WITH NOFORMAT, INIT, NAME = N'<PublishedDatabase, SYSNAME, value>-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10 GO |
Sur l’instance B, celle qui héberge la futur base abonnée, lancer une restauration à partir de la sauvegarde créée précédemment :
1 2 3 4 5 6 7 8 9 10 11 |
USE [master] GO IF EXISTS (SELECT 1 FROM master.sys.databases WHERE name = '<SubscribtionDatabase, SYSNAME, value>') BEGIN ALTER DATABASE [<SubscribtionDatabase, SYSNAME, value>] SET OFFLINE WITH ROLLBACK IMMEDIATE END RESTORE DATABASE [<SubscribtionDatabase, SYSNAME, value>] FROM DISK = N'<BackupFullPath, VARCHAR(500), value>' WITH REPLACE, RECOVERY, FILE = 1, MOVE N'<DbDataName, SYSNAME, value>' TO N'<DbDataPath, VARCHAR(500), value>', MOVE N'<DbLogName, SYSNAME, value>' TO N'<DbLogPath, VARCHAR(500), value>', STATS = 10 |
Lors de la création de l’abonné, il y a deux paramètres à retenir dans la création de l’abonné :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
USE [<PublishedDatabase, SYSNAME, value>] GO EXEC sp_addsubscription @publication = N'<PublicationName, SYSNAME, value>', @subscriber = N'<SubscribtionInstance, SYSNAME, value>', @destination_db = N'<SubscribtionDatabase, SYSNAME, value>', @subscription_type = N'Push', @article = N'all', @update_mode = N'read only', @sync_type = N'initialize with backup', @backupDeviceType = N'Disk', @backupDeviceName = N'<BackupFullPath, VARCHAR(500), value>', @subscriber_type = 0 EXEC sp_addpushsubscription_agent @publication = N'<PublicationName, SYSNAME, value>', @subscriber = N'<SubscribtionInstance, SYSNAME, value>', @subscriber_db = N'<SubscribtionDatabase, SYSNAME, value>', @job_login = null, @job_password = null, @subscriber_security_mode = 1, @frequency_type = 64, @frequency_interval = 1, @frequency_relative_interval = 1, @frequency_recurrence_factor = 0, @frequency_subday = 4, @frequency_subday_interval = 5, @active_start_time_of_day = 0, @active_end_time_of_day = 235959, @active_start_date = 0, @active_end_date = 0, @dts_package_location = N'Distributor' GO |
Retry the operation again with a more up-to-date log, differential, or full database backup
Sur l’instance de distribution, réactiver le job de purge des transactions de la réplication. Comme indiqué précédemment, le nom du job varie suivant le nom de la base de distribution utilisée par la publication.
1 2 |
EXEC msdb..sp_update_job @job_name = 'Distribution clean up: distribution', @enabled = 1; GO |
Sur la base publiée, on relève le nombre de lignes sur l’article répliqué :
1 2 3 4 |
USE [<PublishedDatabase, SYSNAME, value>] GO EXEC sp_spaceused 'dbo.RandomData1' |
Qu’on compare avec la base abonnée :
1 2 3 4 |
USE [<SubscribtionDatabase, SYSNAME, value>] GO EXEC sp_spaceused 'dbo.RandomData1' |
Mais qu’en est-il lorsqu’on ajoute un article dans la publication par la suite, après une initialisation par sauvegarde ? RIEN, Il ne se passe rien, aucune données ne remonte sur l’abonné. Il va falloir modifier quelques paramètres pour rendre la synchronisation de l’abonné fonctionnelle.
Nous allons donc modifier 3 propriétés sur la publication :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
USE [<PublishedDatabase, SYSNAME, value>] GO IF EXISTS (SELECT TOP 1 1 FROM dbo.syspublications WHERE name = '<PublicationName, SYSNAME, value>' AND immediate_sync = 1) BEGIN EXEC sp_changepublication @publication = '<PublicationName, SYSNAME, value>', @property = 'allow_anonymous', @value = 'false' EXEC sp_changepublication @publication = '<PublicationName, SYSNAME, value>', @property = 'immediate_sync' , @value = 'false' END EXEC sp_changepublication @publication = '<PublicationName, SYSNAME, value>', @property = 'allow_initialize_from_backup' , @value = 'false' GO |
La petite touche “Brico Dépôt” : la synchronisation de l’abonné devra être modifiée en réalisant un UPDATE dans une table système liée à la réplication. Cette mise à jour consiste à rendre la synchronisation de l’abonné automatique, comme suit :
1 2 3 4 5 6 7 8 9 10 11 |
USE [<PublishedDatabase, SYSNAME, value>] GO UPDATE su SET sync_type = 1 FROM syspublications sp JOIN sysarticles sa ON sp.pubid = sa.pubid JOIN syssubscriptions su ON sa.artid = su.artid WHERE sp.Name = N'<PublicationName, SYSNAME, value>' AND su.srvname = N'<SubscribtionInstance, SYSNAME, value>' AND su.dest_db = N'<SubscribtionDatabase, SYSNAME, value>' |
La publication et l’abonné sont désormais prêt à recevoir de nouveaux articles !
L’ajout d’article ne diffère en rien (table RandomData2), on procède comme à l’accoutumée avec un refresh de l’abonné et la génération d’un snapshot différentiel en prime :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
USE [<PublishedDatabase, SYSNAME, value>] GO EXEC sp_addarticle @publication = N'<PublicationName, SYSNAME, value>', @article = N'RandomData2', @source_owner = N'dbo', @source_object = N'RandomData2', @type = N'logbased', @description = null, @creation_script = null, @pre_creation_cmd = N'drop', @schema_option = 0x000000000803509F, @identityrangemanagementoption = N'manual', @destination_table = N'RandomData2', @destination_owner = N'dbo', @vertical_partition = N'false', @ins_cmd = N'CALL sp_MSins_dboRandomData2', @del_cmd = N'CALL sp_MSdel_dboRandomData2', @upd_cmd = N'SCALL sp_MSupd_dboRandomData2' GO EXEC sp_refreshsubscriptions @Publication = '<PublicationName, SYSNAME, value>'; EXEC sp_startpublication_snapshot @publication = '<PublicationName, SYSNAME, value>'; GO |
Sur la base publiée, on relève le nombre de lignes sur l’article répliqué en seconde noce :
1 2 3 4 |
USE [<PublishedDatabase, SYSNAME, value>] GO EXEC sp_spaceused 'dbo.RandomData2' |
1 2 3 4 |
USE [<SubscribtionDatabase, SYSNAME, value>] GO EXEC sp_spaceused 'dbo.RandomData2' |