You are here:   Blog
Register   |  Login

Blog Archive:

* Can be used in order to search for older blogs Entries

Search in blogs

Blog Categories:

* Can be used in order to search for blogs Entries by Categories

Blog Tags:

* Can be used in order to search for blogs by keywords


Awared MVP


Microsoft® Community Contributor 

Microsoft® Community Contributor

 Read this before you use the blog! Maximize

Recent Entries


Written by: ronen ariely
04/10/2021 01:02 RssIcon

This is an answer to a question in the Microsoft's QnA forum. As a result of a bug in the forum, I could not publish the answer

Original question is here:


-------------------- Question ---------------------

I'm setting up transactional replication for multiple databases in SQL Server where the publisher is on SQL Server 2014 Enterprise Edition and the target is SQL Server 2017 Standard. All but 2 of the databases work great, and the error I'm running into is right at the end of applying the snapshot to the subscriber for those 2.

  • "Online index operations can only be performed in Enterprise edition of SQL Server"

I'm assuming this means that those 2 databases have at one point applied indexing or changes with ONLINE=ON? I have attempted to create the snapshot without the clustered and nonclustered indexes and keep hitting the error as well.

If this is true, Is there a way to tell which articles in the database have been scripted with ONLINE=ON to cause this error to pop up?

I'm planning a full index rebuild with ONLINE=OFF on the smaller of the source databases hoping that is the workaround but am curious if anyone out there knows the solution. Would hate to have to be forced to get Enterprise Edition just because of this error!

UPDATE: The index rebuild did not get me away from the error. Is there a method by chance to do transactional replication without the primary key indexing being applied?

-------------------- Answer ---------------------


Finally we have excuse to dig to the internals

> I'm assuming this means that those 2 databases have at one point applied indexing or changes with ONLINE=ON?

This make sense...

But remember that "Online index operations" can include multiple tasks like CREATE/ALTER/DROP INDEX; ALTER TABLE (add or drop UNIQUE or PRIMARY KEY constraints with CLUSTERED index option and alter columns).

> am curious if anyone out there knows the solution

For **first step, we need to find who/how/which task** was executed and used ONLINE which lead to this issue. Meaning we need to find the task which was used ONLINE and block you from using none-Enterprise edition.

**Note!** For the sake of the discussion I will speak about **CREATE INDEX** but the same procedure can be applied to other ONLINE tasks, which are logged in the transaction log. 

This might be a bit complex and advance, since the information about how we created the index, **is not stored in the database tables** (metadata) as much as remember since once it was created it has no value/uses (usually, since as we found now it might have a value in this question). 

Therefore, **we need to pull the information from the transaction log** exactly from the same place that the transaction replication takes it from.

For this task we will use the undocumented function fn_dblog

Lets demonstrate it from start to end

 USE Test
 DROP TABLE IF EXISTS fyi_links_indexed
 Create table fyi_links_indexed(url char(100))
 GO -- No index yet
 -- Let's find the last LSN from the log file.
 -- This will help us to filter the log file in our sample instead of read the entire log file each time.
 SELECT TOP 1 [Current LSN]
 FROM fn_dblog(null,null)
 GO -- remember this value for next step. In my test Db I got 00000025:00000a18:0024
 -- For each query now we will use: where [Current LSN] > '00000025:00000a18:0024'
 -- Let's CREATE INDEX ONLINE and check what was added in the transaction log
 CREATE INDEX fyi_links_url ON fyi_links_indexed (url)
 -- just for understanding let's see waht added to the transaction log:
 SELECT * FROM fn_dblog(null,null)
 where [Current LSN] > '00000025:00000a18:0024'
 -- Notice that creating the index online writes multiple rows in the transaction log
 -- just for understanding let's confirm that this index was created ONLINe,
 -- which is what the replication see as well
 -- and what failed in the standards edidion
 SELECT [Current LSN], [Transaction ID], [Transaction Name] FROM fn_dblog(null,null)
 where [Current LSN] > '00000025:00000a18:0024' and [Transaction Name] in('CREATE INDEX','ONLINE_INDEX_DDL')
 -- our next task is to find the transaction ID
 -- search for Transaction name "CREATE INDEX"
 SELECT [Transaction ID], [Transaction Name] FROM fn_dblog(null,null)
 where [Current LSN] > '00000025:00000a18:0024' and Operation = 'LOP_BEGIN_XACT' and [Transaction Name] = 'CREATE INDEX'
 GO -- 0000:0000042d
 -- Now we have the transaction ID so we can get the object ID if the entitiy which was used ONLINE (in our case the INDEX)
 select [Lock Information]
 FROM fn_dblog(null,null) where [Transaction ID] = '0000:0000042d' and [Lock Information] like '%object_id = %'
 -- result should be like:
 -- HoBt 0:ACQUIRE_LOCK_SCH_M METADATA: database_id = 9 INDEXSTATS(object_id = 901578250, index_id or stats_id = 2), lockPartitionId = 0
 -- Notice the object Id (in my case 901578250)

That is all! 

We found the id of the problematic entity which was created using ONLINE

What next?

The problem is that cleaning the information from the transaction log in ordefr to continue the replication as it is, might be very advance and complexs (can be done in theory)

SO **at this point we know what to fix** but the next question is how to fix...

The simples way is to start the replication from this point after the problematic entity was already created since if we use the current transaction log then the issue will appear even if we drop the the index now since the replication is done log by log and once we will use the log that include the `CREATE INDEX ONLINE` then the issue will appear again.

After you create the replication then we need to avoid related issue in the future

In production the execution of index rebuild (which can use ONLINE) for example, is usually done by manual exaction or maintenance tasks which executed in Jobs (Unfortunately it can come from other triggers which are more difficult to find if you are not familiar with the system).

At this point you must confirm that if your duplicated the same jobs from the source server then you should fix all these jobs and other task which use ONLINE.

This is a separate task

### find jobs which include the world ONLINE

    s.step_id as 'Step ID',
    j.[name] as 'SQL Agent Job Name',
    s.database_name as 'DB Name',
    s.command as 'Command'
FROM   msdb.dbo.sysjobsteps AS s
INNER JOIN msdb.dbo.sysjobs AS j ON  s.job_id = j.job_id
WHERE  s.command LIKE '%ONLINE%'

### Last question we has here

> Is there a method by chance to do transactional replication without the primary key indexing being applied?

No. Transactional replication requires a primary key constraint on each published table.