Skip to content

Commit

Permalink
Fix concurrency issue on user create/update
Browse files Browse the repository at this point in the history
When creating or updating a mssql user in servers with compatibility
level below 130, the `string_split` function needs to be created.

At the moment, this is causing issues when trying to create/update
multiple users, as each of those requests tries to create the same
function.

This change introduces a lock on application resource in exclusive
mode so that only one of the calls creates the function.

Fixes betr-io#31
  • Loading branch information
beandrad committed Dec 14, 2022
1 parent f00d2b9 commit d5e3afe
Showing 1 changed file with 16 additions and 6 deletions.
22 changes: 16 additions & 6 deletions sql/user.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package sql

import (
"context"
"database/sql"
"strings"
"github.com/betr-io/terraform-provider-mssql/mssql/model"
"context"
"database/sql"
"github.com/betr-io/terraform-provider-mssql/mssql/model"
"strings"
)

func (c *Connector) GetUser(ctx context.Context, database, username string) (*model.User, error) {
Expand Down Expand Up @@ -120,7 +120,10 @@ func (c *Connector) CreateUser(ctx context.Context, database string, user *model
'DEFAULT_LANGUAGE = ' + Coalesce(QuoteName(@language), 'NONE')
END
END
IF exists (select compatibility_level FROM sys.databases where name = db_name() and compatibility_level < 130)
BEGIN TRANSACTION;
EXEC sp_getapplock @Resource = 'create_func', @LockMode = 'Exclusive';
IF exists (select compatibility_level FROM sys.databases where name = db_name() and compatibility_level < 130) AND objectproperty(object_id('String_Split'), 'isProcedure') IS NULL
BEGIN
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'Create FUNCTION [dbo].[String_Split]
Expand All @@ -146,6 +149,8 @@ func (c *Connector) CreateUser(ctx context.Context, database string, user *model
)';
EXEC sp_executesql @sql;
END
EXEC sp_releaseapplock @Resource = 'create_func';
COMMIT TRANSACTION;
SET @stmt = @stmt + '; ' +
'DECLARE role_cur CURSOR FOR SELECT name FROM ' + QuoteName(@database) + '.[sys].[database_principals] WHERE type = ''R'' AND name != ''public'' AND name COLLATE SQL_Latin1_General_CP1_CI_AS IN (SELECT value FROM String_Split(' + QuoteName(@roles, '''') + ', '',''));' +
'DECLARE @role nvarchar(max);' +
Expand Down Expand Up @@ -194,7 +199,10 @@ func (c *Connector) UpdateUser(ctx context.Context, database string, user *model
BEGIN
SET @stmt = @stmt + ', DEFAULT_LANGUAGE = ' + Coalesce(QuoteName(@language), 'NONE')
END
IF exists (select compatibility_level FROM sys.databases where name = db_name() and compatibility_level < 130)
EXEC sp_getapplock @Resource = 'check_timing', @LockMode = 'Exclusive';
BEGIN TRANSACTION;
EXEC sp_getapplock @Resource = 'create_func', @LockMode = 'Exclusive';
IF exists (select compatibility_level FROM sys.databases where name = db_name() and compatibility_level < 130) AND objectproperty(object_id('String_Split'), 'isProcedure') IS NULL
BEGIN
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'Create FUNCTION [dbo].[String_Split]
Expand All @@ -220,6 +228,8 @@ func (c *Connector) UpdateUser(ctx context.Context, database string, user *model
)';
EXEC sp_executesql @sql;
END
EXEC sp_releaseapplock @Resource = 'create_func';
COMMIT TRANSACTION;
SET @stmt = @stmt + '; ' +
'DECLARE @sql nvarchar(max);' +
'DECLARE @role nvarchar(max);' +
Expand Down

0 comments on commit d5e3afe

Please sign in to comment.