I'm trying to rebuild the clustered index of a large table (77GB) on Azure SQL Database. There is high concurrent transactional activity on the table so I'm using the ONLINE=ON option.
This has worked well for smaller tables; however, when I run it on this large table it seems to take exclusive locks on the table. I had to stop it after 5 minutes because all transactional activity was timing out.
From session with SPID 199:
ALTER INDEX PK_Customer ON [br].[Customer]
REBUILD WITH (ONLINE = ON, RESUMABLE = ON);
From another session:
A bit further below in the same results:
- Object 978102525 is the clustered index.
- Object 1125579048 is the table.
I understand that online rebuild can take locks for a 'short' duration at the start and end of the process. However, these locks are taken for several minutes, which is not exactly a 'short' duration.
Additional info
While the rebuild is running, I ran SELECT * FROM sys.index_resumable_operations; but it returned 0 rows, as if the rebuild hadn't started at all.
The smaller tables also have a PK potentially > 900 bytes and the same ALTER statement worked without any long blocking so I don't think it's related to PK size. These smaller tables also had a similar amounts of nvarchar(max) columns. The only real difference I can think of is that this table has many more rows.
Table definition
Here is the full definition of br.Customer. There are no foreign keys or non clustered indices.
CREATE TABLE [br].[Customer](
[Id] [bigint] NOT NULL,
[ShopId] [nvarchar](450) NOT NULL,
[accepts_marketing] [bit] NOT NULL,
[address1] [nvarchar](max) MASKED WITH (FUNCTION = 'partial(2, "XXX", 0)') NULL,
[address2] [nvarchar](max) MASKED WITH (FUNCTION = 'partial(2, "XXX", 0)') NULL,
[city] [nvarchar](max) NULL,
[company] [nvarchar](max) NULL,
[country] [nvarchar](max) NULL,
[country_code] [nvarchar](max) NULL,
[email] [nvarchar](max) MASKED WITH (FUNCTION = 'email()') NULL,
[first_name] [nvarchar](max) MASKED WITH (FUNCTION = 'partial(2, "XXX", 0)') NULL,
[last_name] [nvarchar](max) MASKED WITH (FUNCTION = 'partial(2, "XXX", 0)') NULL,
[note] [nvarchar](max) NULL,
[phone] [nvarchar](max) MASKED WITH (FUNCTION = 'partial(2, "XXX", 0)') NULL,
[province] [nvarchar](max) NULL,
[province_code] [nvarchar](max) NULL,
[state] [nvarchar](max) NULL,
[tax_exempt] [bit] NOT NULL,
[verified_email] [bit] NOT NULL,
[zip] [nvarchar](max) NULL,
[multipass_identifier] [nvarchar](max) NULL,
[created_at_local] [datetimeoffset](7) NOT NULL,
[updated_at_local] [datetimeoffset](7) NOT NULL,
[tags] [nvarchar](max) NULL,
[address_phone] [nvarchar](max) MASKED WITH (FUNCTION = 'partial(2, "XXX", 0)') NULL,
[address_firstname] [nvarchar](max) MASKED WITH (FUNCTION = 'partial(2, "XXX", 0)') NULL,
[address_lastname] [nvarchar](max) MASKED WITH (FUNCTION = 'partial(2, "XXX", 0)') NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[ShopId] ASC,
[Id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [br].[Customer] ADD DEFAULT ('0001-01-01T00:00:00.000+00:00') FOR [created_at_local]
GO
ALTER TABLE [br].[Customer] ADD DEFAULT ('0001-01-01T00:00:00.000+00:00') FOR [updated_at_local]
GO
sp_WhoIsActive
I investigated further today (24 Sep) and ran SP_WHOISACTIVE @get_locks = 1, which clearly shows all UPDATE/INSERT/DELETE transactions blocked by the session running the ALTER INDEX.
Locks held on Customer table by query running the ALTER INDEX:
<Object name="Customer" schema_name="br">
<Locks>
<Lock resource_type="METADATA.INDEXSTATS" request_mode="Sch-S" request_status="GRANT" request_count="1" />
<Lock resource_type="METADATA.INDEXSTATS" index_name="PK_Customer" request_mode="Sch-S" request_status="GRANT" request_count="1" />
<Lock resource_type="METADATA.STATS" request_mode="Sch-S" request_status="GRANT" request_count="1" />
<Lock resource_type="OBJECT" request_mode="S" request_status="GRANT" request_count="96" />
<Lock resource_type="OBJECT" request_mode="X" request_status="GRANT" request_count="96" />
<Lock resource_type="OBJECT.INDEX_OPERATION" request_mode="Sch-M" request_status="GRANT" request_count="1" />
</Locks>
</Object>
<Object name="PK_Customer" schema_name="br">
<Locks>
<Lock resource_type="OBJECT" request_mode="Sch-S" request_status="GRANT" request_count="1" />
</Locks>
</Object>
Locks from session running UPDATE on same table:
<Object name="Customer" schema_name="br">
<Locks>
<Lock resource_type="OBJECT" request_mode="IX" request_status="WAIT" request_count="1" />
</Locks>
</Object>


