Apparently, my CLR assembly function is causing deadlocks?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}
Our application needs to work equally well with an Oracle database or a Microsoft SQL Server database. To facilitate this, we created a handful of UDFs to homogenize our query syntax. For example, SQL Server has GETDATE() and Oracle has SYSDATE. They perform the same function but they are different words. We wrote a wrapper UDF called NOW() for both platforms which wraps the relevant platform specific syntax in a common function name. We have other such functions, some of which do essentially nothing but exist solely for the sake of homogenization. Unfortunately, this has a cost for SQL Server. Inline scalar UDFs wreak havoc on performance and completely disable parallelism. As an alternative, we wrote CLR assembly functions to accomplish the same goals. When we deployed this to a client they started experiencing frequent deadlocks. This particular client is using replication and high availability techniques and I'm wondering if there's some sort of interaction going on here. I just don't understand how introducing a CLR function would cause problems like this. For reference, I've included the original scalar UDF definition as well as the replacement CLR definition in C# and the SQL declaration for it. I also have deadlock XML that I can provide if that helps.
Original UDF
CREATE FUNCTION [fn].[APAD]
(
@Value VARCHAR(4000)
, @tablename VARCHAR(4000) = NULL
, @columnname VARCHAR(4000) = NULL
)
RETURNS VARCHAR(4000)
WITH SCHEMABINDING
AS
BEGIN
RETURN LTRIM(RTRIM(@Value))
END
GO
CLR Assembly Function
[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
return value?.Trim();
}
SQL Server Declaration for CLR Function
CREATE FUNCTION [fn].[APAD]
(
@Value NVARCHAR(4000),
@TableName NVARCHAR(4000),
@ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
sql-server functions deadlock sql-clr scalar-function
New contributor
add a comment |
Our application needs to work equally well with an Oracle database or a Microsoft SQL Server database. To facilitate this, we created a handful of UDFs to homogenize our query syntax. For example, SQL Server has GETDATE() and Oracle has SYSDATE. They perform the same function but they are different words. We wrote a wrapper UDF called NOW() for both platforms which wraps the relevant platform specific syntax in a common function name. We have other such functions, some of which do essentially nothing but exist solely for the sake of homogenization. Unfortunately, this has a cost for SQL Server. Inline scalar UDFs wreak havoc on performance and completely disable parallelism. As an alternative, we wrote CLR assembly functions to accomplish the same goals. When we deployed this to a client they started experiencing frequent deadlocks. This particular client is using replication and high availability techniques and I'm wondering if there's some sort of interaction going on here. I just don't understand how introducing a CLR function would cause problems like this. For reference, I've included the original scalar UDF definition as well as the replacement CLR definition in C# and the SQL declaration for it. I also have deadlock XML that I can provide if that helps.
Original UDF
CREATE FUNCTION [fn].[APAD]
(
@Value VARCHAR(4000)
, @tablename VARCHAR(4000) = NULL
, @columnname VARCHAR(4000) = NULL
)
RETURNS VARCHAR(4000)
WITH SCHEMABINDING
AS
BEGIN
RETURN LTRIM(RTRIM(@Value))
END
GO
CLR Assembly Function
[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
return value?.Trim();
}
SQL Server Declaration for CLR Function
CREATE FUNCTION [fn].[APAD]
(
@Value NVARCHAR(4000),
@TableName NVARCHAR(4000),
@ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
sql-server functions deadlock sql-clr scalar-function
New contributor
9
Deterministic scalar CLR functions should not contribute to deadlocks. Of course CLR functions that read the database might. You should include the deadlock XML in your question.
– David Browne - Microsoft
11 hours ago
add a comment |
Our application needs to work equally well with an Oracle database or a Microsoft SQL Server database. To facilitate this, we created a handful of UDFs to homogenize our query syntax. For example, SQL Server has GETDATE() and Oracle has SYSDATE. They perform the same function but they are different words. We wrote a wrapper UDF called NOW() for both platforms which wraps the relevant platform specific syntax in a common function name. We have other such functions, some of which do essentially nothing but exist solely for the sake of homogenization. Unfortunately, this has a cost for SQL Server. Inline scalar UDFs wreak havoc on performance and completely disable parallelism. As an alternative, we wrote CLR assembly functions to accomplish the same goals. When we deployed this to a client they started experiencing frequent deadlocks. This particular client is using replication and high availability techniques and I'm wondering if there's some sort of interaction going on here. I just don't understand how introducing a CLR function would cause problems like this. For reference, I've included the original scalar UDF definition as well as the replacement CLR definition in C# and the SQL declaration for it. I also have deadlock XML that I can provide if that helps.
Original UDF
CREATE FUNCTION [fn].[APAD]
(
@Value VARCHAR(4000)
, @tablename VARCHAR(4000) = NULL
, @columnname VARCHAR(4000) = NULL
)
RETURNS VARCHAR(4000)
WITH SCHEMABINDING
AS
BEGIN
RETURN LTRIM(RTRIM(@Value))
END
GO
CLR Assembly Function
[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
return value?.Trim();
}
SQL Server Declaration for CLR Function
CREATE FUNCTION [fn].[APAD]
(
@Value NVARCHAR(4000),
@TableName NVARCHAR(4000),
@ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
sql-server functions deadlock sql-clr scalar-function
New contributor
Our application needs to work equally well with an Oracle database or a Microsoft SQL Server database. To facilitate this, we created a handful of UDFs to homogenize our query syntax. For example, SQL Server has GETDATE() and Oracle has SYSDATE. They perform the same function but they are different words. We wrote a wrapper UDF called NOW() for both platforms which wraps the relevant platform specific syntax in a common function name. We have other such functions, some of which do essentially nothing but exist solely for the sake of homogenization. Unfortunately, this has a cost for SQL Server. Inline scalar UDFs wreak havoc on performance and completely disable parallelism. As an alternative, we wrote CLR assembly functions to accomplish the same goals. When we deployed this to a client they started experiencing frequent deadlocks. This particular client is using replication and high availability techniques and I'm wondering if there's some sort of interaction going on here. I just don't understand how introducing a CLR function would cause problems like this. For reference, I've included the original scalar UDF definition as well as the replacement CLR definition in C# and the SQL declaration for it. I also have deadlock XML that I can provide if that helps.
Original UDF
CREATE FUNCTION [fn].[APAD]
(
@Value VARCHAR(4000)
, @tablename VARCHAR(4000) = NULL
, @columnname VARCHAR(4000) = NULL
)
RETURNS VARCHAR(4000)
WITH SCHEMABINDING
AS
BEGIN
RETURN LTRIM(RTRIM(@Value))
END
GO
CLR Assembly Function
[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
return value?.Trim();
}
SQL Server Declaration for CLR Function
CREATE FUNCTION [fn].[APAD]
(
@Value NVARCHAR(4000),
@TableName NVARCHAR(4000),
@ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
sql-server functions deadlock sql-clr scalar-function
sql-server functions deadlock sql-clr scalar-function
New contributor
New contributor
edited 11 hours ago
Solomon Rutzky
49.9k585184
49.9k585184
New contributor
asked 11 hours ago
Russ SuterRuss Suter
361
361
New contributor
New contributor
9
Deterministic scalar CLR functions should not contribute to deadlocks. Of course CLR functions that read the database might. You should include the deadlock XML in your question.
– David Browne - Microsoft
11 hours ago
add a comment |
9
Deterministic scalar CLR functions should not contribute to deadlocks. Of course CLR functions that read the database might. You should include the deadlock XML in your question.
– David Browne - Microsoft
11 hours ago
9
9
Deterministic scalar CLR functions should not contribute to deadlocks. Of course CLR functions that read the database might. You should include the deadlock XML in your question.
– David Browne - Microsoft
11 hours ago
Deterministic scalar CLR functions should not contribute to deadlocks. Of course CLR functions that read the database might. You should include the deadlock XML in your question.
– David Browne - Microsoft
11 hours ago
add a comment |
1 Answer
1
active
oldest
votes
What version(s) of SQL Server are you using?
I do recall seeing a slight change in behavior in SQL Server 2017 not too long ago. I will have to go back and see if I can find where I made a note of it, but I think it had to do with a schema lock being initiated when a SQLCLR object was being accessed.
While I am looking for that, I will say the following regarding your approach:
- Please use the
Sql*
types for input parameters, return types. You should be usingSqlString
instead ofstring
.SqlString
is very similar to a nullable string (yourvalue?
, but it has other functionality built in that is SQL Server-specific. All of theSql*
types have aValue
property that returns the expected .NET type (e.g.SqlString.Value
returnsstring
,SqlInt32
returnsint
,SqlDateTime
returnsDateTime
, etc).
I would recommend against this entire approach to begin with, whether or not the deadlocks are related. I say this because:
- Even with deterministic SQLCLR UDFs being able to participate in parallel plans, you are most likely going to get performance hits for emulating simplistic built-in functions.
- The SQLCLR API does not allow for
VARCHAR
. Are you ok with implicitly converting everything toNVARCHAR
and then again back toVARCHAR
for simple operations? - The SQLCLR API does not allow for overloading, so you might need multiple versions of functions that do allow for different signatures in T-SQL and/or PL/SQL.
- Similar to not allowing for overloading, there is a big difference between
NVARCHAR(4000)
andNVARCHAR(MAX)
: theMAX
type (having even a single one of them in the signature) make the SQLCLR call take twice as long as not having anyMAX
type in the signature (I believe this holds true forVARBINARY(MAX)
vsVARBINARY(4000)
as well). So, you need to decide between:
- using only
NVARCHAR(MAX)
to have a simplified API, but take the performance hit when you are using 8000 bytes or less of string data, or - creating two variations for all / most / many string functions: one with
MAX
types, and one without (for when you are guaranteed to never go over 8000 bytes of string data in or out). This is the approach I chose to take for most of the functions in my SQL# library: there is aTrim()
function which likely has one or moreMAX
types, and aTrim4k()
version which never has aMAX
type anywhere in the signature or result set schema. The "4k" versions are absolutely more efficient.
- using only
You are not being careful to emulate functionality given the example in the question.
LTRIM
andRTRIM
only trim spaces, while .NETString.Trim()
trims white space (at least space, tabs, and newlines). For example:
PRINT LTRIM(RTRIM(N' a '));
- Also, I just noticed that your function, both in T-SQL and in C#, only uses 1 of the 3 input parameters. Is this just a proof of concept, or redacted code?
1. Thanks for the tip on using the Sql types. I'll make that change now. 2. There are external forces at work here which necessitate the use of them. I'm not thrilled about it but trust me, it's better than the alternative. My original question contains a little bit of the explanation as to why a seemingly asinine function exists and is being used.
– Russ Suter
10 hours ago
@RussSuter Understood re: external forces. I was just pointing out some pitfalls that might not have been known when that decision was made. Either way, I'm unable to find my notes or reproduce the scenario from the few details I remember of it. I just remember something definitely changing in 2017 with regards to transactions and calling code from an assembly, and being really annoyed with it as it seemed like a needless change for the worse, and I had to work around it for what I was testing that worked fine in prior versions. So, please post a link in the question to the deadlock XML.
– Solomon Rutzky
10 hours ago
Thanks for that additional info. Here's a link to the XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
– Russ Suter
8 hours ago
@RussSuter Have you tried this with inlining the T-SQL? Looking at the deadlock XML (which is not easy as it's a single line--all newlines got removed somehow) it appears to be a series of PAGE locks between sessions 60 and 78. There are 8 pages locked between both sessions: 3 for one SPID and 5 for the other. Each with a different process ID, so this is an issue of parallelism. If this is related to SQLCLR, it might ironically be the fact that SQLCLR isn't preventing parallelism. This is why I asked if you have tried putting the simple function inline as that might also show the deadlock.
– Solomon Rutzky
8 hours ago
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "182"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Russ Suter is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f236794%2fapparently-my-clr-assembly-function-is-causing-deadlocks%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
What version(s) of SQL Server are you using?
I do recall seeing a slight change in behavior in SQL Server 2017 not too long ago. I will have to go back and see if I can find where I made a note of it, but I think it had to do with a schema lock being initiated when a SQLCLR object was being accessed.
While I am looking for that, I will say the following regarding your approach:
- Please use the
Sql*
types for input parameters, return types. You should be usingSqlString
instead ofstring
.SqlString
is very similar to a nullable string (yourvalue?
, but it has other functionality built in that is SQL Server-specific. All of theSql*
types have aValue
property that returns the expected .NET type (e.g.SqlString.Value
returnsstring
,SqlInt32
returnsint
,SqlDateTime
returnsDateTime
, etc).
I would recommend against this entire approach to begin with, whether or not the deadlocks are related. I say this because:
- Even with deterministic SQLCLR UDFs being able to participate in parallel plans, you are most likely going to get performance hits for emulating simplistic built-in functions.
- The SQLCLR API does not allow for
VARCHAR
. Are you ok with implicitly converting everything toNVARCHAR
and then again back toVARCHAR
for simple operations? - The SQLCLR API does not allow for overloading, so you might need multiple versions of functions that do allow for different signatures in T-SQL and/or PL/SQL.
- Similar to not allowing for overloading, there is a big difference between
NVARCHAR(4000)
andNVARCHAR(MAX)
: theMAX
type (having even a single one of them in the signature) make the SQLCLR call take twice as long as not having anyMAX
type in the signature (I believe this holds true forVARBINARY(MAX)
vsVARBINARY(4000)
as well). So, you need to decide between:
- using only
NVARCHAR(MAX)
to have a simplified API, but take the performance hit when you are using 8000 bytes or less of string data, or - creating two variations for all / most / many string functions: one with
MAX
types, and one without (for when you are guaranteed to never go over 8000 bytes of string data in or out). This is the approach I chose to take for most of the functions in my SQL# library: there is aTrim()
function which likely has one or moreMAX
types, and aTrim4k()
version which never has aMAX
type anywhere in the signature or result set schema. The "4k" versions are absolutely more efficient.
- using only
You are not being careful to emulate functionality given the example in the question.
LTRIM
andRTRIM
only trim spaces, while .NETString.Trim()
trims white space (at least space, tabs, and newlines). For example:
PRINT LTRIM(RTRIM(N' a '));
- Also, I just noticed that your function, both in T-SQL and in C#, only uses 1 of the 3 input parameters. Is this just a proof of concept, or redacted code?
1. Thanks for the tip on using the Sql types. I'll make that change now. 2. There are external forces at work here which necessitate the use of them. I'm not thrilled about it but trust me, it's better than the alternative. My original question contains a little bit of the explanation as to why a seemingly asinine function exists and is being used.
– Russ Suter
10 hours ago
@RussSuter Understood re: external forces. I was just pointing out some pitfalls that might not have been known when that decision was made. Either way, I'm unable to find my notes or reproduce the scenario from the few details I remember of it. I just remember something definitely changing in 2017 with regards to transactions and calling code from an assembly, and being really annoyed with it as it seemed like a needless change for the worse, and I had to work around it for what I was testing that worked fine in prior versions. So, please post a link in the question to the deadlock XML.
– Solomon Rutzky
10 hours ago
Thanks for that additional info. Here's a link to the XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
– Russ Suter
8 hours ago
@RussSuter Have you tried this with inlining the T-SQL? Looking at the deadlock XML (which is not easy as it's a single line--all newlines got removed somehow) it appears to be a series of PAGE locks between sessions 60 and 78. There are 8 pages locked between both sessions: 3 for one SPID and 5 for the other. Each with a different process ID, so this is an issue of parallelism. If this is related to SQLCLR, it might ironically be the fact that SQLCLR isn't preventing parallelism. This is why I asked if you have tried putting the simple function inline as that might also show the deadlock.
– Solomon Rutzky
8 hours ago
add a comment |
What version(s) of SQL Server are you using?
I do recall seeing a slight change in behavior in SQL Server 2017 not too long ago. I will have to go back and see if I can find where I made a note of it, but I think it had to do with a schema lock being initiated when a SQLCLR object was being accessed.
While I am looking for that, I will say the following regarding your approach:
- Please use the
Sql*
types for input parameters, return types. You should be usingSqlString
instead ofstring
.SqlString
is very similar to a nullable string (yourvalue?
, but it has other functionality built in that is SQL Server-specific. All of theSql*
types have aValue
property that returns the expected .NET type (e.g.SqlString.Value
returnsstring
,SqlInt32
returnsint
,SqlDateTime
returnsDateTime
, etc).
I would recommend against this entire approach to begin with, whether or not the deadlocks are related. I say this because:
- Even with deterministic SQLCLR UDFs being able to participate in parallel plans, you are most likely going to get performance hits for emulating simplistic built-in functions.
- The SQLCLR API does not allow for
VARCHAR
. Are you ok with implicitly converting everything toNVARCHAR
and then again back toVARCHAR
for simple operations? - The SQLCLR API does not allow for overloading, so you might need multiple versions of functions that do allow for different signatures in T-SQL and/or PL/SQL.
- Similar to not allowing for overloading, there is a big difference between
NVARCHAR(4000)
andNVARCHAR(MAX)
: theMAX
type (having even a single one of them in the signature) make the SQLCLR call take twice as long as not having anyMAX
type in the signature (I believe this holds true forVARBINARY(MAX)
vsVARBINARY(4000)
as well). So, you need to decide between:
- using only
NVARCHAR(MAX)
to have a simplified API, but take the performance hit when you are using 8000 bytes or less of string data, or - creating two variations for all / most / many string functions: one with
MAX
types, and one without (for when you are guaranteed to never go over 8000 bytes of string data in or out). This is the approach I chose to take for most of the functions in my SQL# library: there is aTrim()
function which likely has one or moreMAX
types, and aTrim4k()
version which never has aMAX
type anywhere in the signature or result set schema. The "4k" versions are absolutely more efficient.
- using only
You are not being careful to emulate functionality given the example in the question.
LTRIM
andRTRIM
only trim spaces, while .NETString.Trim()
trims white space (at least space, tabs, and newlines). For example:
PRINT LTRIM(RTRIM(N' a '));
- Also, I just noticed that your function, both in T-SQL and in C#, only uses 1 of the 3 input parameters. Is this just a proof of concept, or redacted code?
1. Thanks for the tip on using the Sql types. I'll make that change now. 2. There are external forces at work here which necessitate the use of them. I'm not thrilled about it but trust me, it's better than the alternative. My original question contains a little bit of the explanation as to why a seemingly asinine function exists and is being used.
– Russ Suter
10 hours ago
@RussSuter Understood re: external forces. I was just pointing out some pitfalls that might not have been known when that decision was made. Either way, I'm unable to find my notes or reproduce the scenario from the few details I remember of it. I just remember something definitely changing in 2017 with regards to transactions and calling code from an assembly, and being really annoyed with it as it seemed like a needless change for the worse, and I had to work around it for what I was testing that worked fine in prior versions. So, please post a link in the question to the deadlock XML.
– Solomon Rutzky
10 hours ago
Thanks for that additional info. Here's a link to the XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
– Russ Suter
8 hours ago
@RussSuter Have you tried this with inlining the T-SQL? Looking at the deadlock XML (which is not easy as it's a single line--all newlines got removed somehow) it appears to be a series of PAGE locks between sessions 60 and 78. There are 8 pages locked between both sessions: 3 for one SPID and 5 for the other. Each with a different process ID, so this is an issue of parallelism. If this is related to SQLCLR, it might ironically be the fact that SQLCLR isn't preventing parallelism. This is why I asked if you have tried putting the simple function inline as that might also show the deadlock.
– Solomon Rutzky
8 hours ago
add a comment |
What version(s) of SQL Server are you using?
I do recall seeing a slight change in behavior in SQL Server 2017 not too long ago. I will have to go back and see if I can find where I made a note of it, but I think it had to do with a schema lock being initiated when a SQLCLR object was being accessed.
While I am looking for that, I will say the following regarding your approach:
- Please use the
Sql*
types for input parameters, return types. You should be usingSqlString
instead ofstring
.SqlString
is very similar to a nullable string (yourvalue?
, but it has other functionality built in that is SQL Server-specific. All of theSql*
types have aValue
property that returns the expected .NET type (e.g.SqlString.Value
returnsstring
,SqlInt32
returnsint
,SqlDateTime
returnsDateTime
, etc).
I would recommend against this entire approach to begin with, whether or not the deadlocks are related. I say this because:
- Even with deterministic SQLCLR UDFs being able to participate in parallel plans, you are most likely going to get performance hits for emulating simplistic built-in functions.
- The SQLCLR API does not allow for
VARCHAR
. Are you ok with implicitly converting everything toNVARCHAR
and then again back toVARCHAR
for simple operations? - The SQLCLR API does not allow for overloading, so you might need multiple versions of functions that do allow for different signatures in T-SQL and/or PL/SQL.
- Similar to not allowing for overloading, there is a big difference between
NVARCHAR(4000)
andNVARCHAR(MAX)
: theMAX
type (having even a single one of them in the signature) make the SQLCLR call take twice as long as not having anyMAX
type in the signature (I believe this holds true forVARBINARY(MAX)
vsVARBINARY(4000)
as well). So, you need to decide between:
- using only
NVARCHAR(MAX)
to have a simplified API, but take the performance hit when you are using 8000 bytes or less of string data, or - creating two variations for all / most / many string functions: one with
MAX
types, and one without (for when you are guaranteed to never go over 8000 bytes of string data in or out). This is the approach I chose to take for most of the functions in my SQL# library: there is aTrim()
function which likely has one or moreMAX
types, and aTrim4k()
version which never has aMAX
type anywhere in the signature or result set schema. The "4k" versions are absolutely more efficient.
- using only
You are not being careful to emulate functionality given the example in the question.
LTRIM
andRTRIM
only trim spaces, while .NETString.Trim()
trims white space (at least space, tabs, and newlines). For example:
PRINT LTRIM(RTRIM(N' a '));
- Also, I just noticed that your function, both in T-SQL and in C#, only uses 1 of the 3 input parameters. Is this just a proof of concept, or redacted code?
What version(s) of SQL Server are you using?
I do recall seeing a slight change in behavior in SQL Server 2017 not too long ago. I will have to go back and see if I can find where I made a note of it, but I think it had to do with a schema lock being initiated when a SQLCLR object was being accessed.
While I am looking for that, I will say the following regarding your approach:
- Please use the
Sql*
types for input parameters, return types. You should be usingSqlString
instead ofstring
.SqlString
is very similar to a nullable string (yourvalue?
, but it has other functionality built in that is SQL Server-specific. All of theSql*
types have aValue
property that returns the expected .NET type (e.g.SqlString.Value
returnsstring
,SqlInt32
returnsint
,SqlDateTime
returnsDateTime
, etc).
I would recommend against this entire approach to begin with, whether or not the deadlocks are related. I say this because:
- Even with deterministic SQLCLR UDFs being able to participate in parallel plans, you are most likely going to get performance hits for emulating simplistic built-in functions.
- The SQLCLR API does not allow for
VARCHAR
. Are you ok with implicitly converting everything toNVARCHAR
and then again back toVARCHAR
for simple operations? - The SQLCLR API does not allow for overloading, so you might need multiple versions of functions that do allow for different signatures in T-SQL and/or PL/SQL.
- Similar to not allowing for overloading, there is a big difference between
NVARCHAR(4000)
andNVARCHAR(MAX)
: theMAX
type (having even a single one of them in the signature) make the SQLCLR call take twice as long as not having anyMAX
type in the signature (I believe this holds true forVARBINARY(MAX)
vsVARBINARY(4000)
as well). So, you need to decide between:
- using only
NVARCHAR(MAX)
to have a simplified API, but take the performance hit when you are using 8000 bytes or less of string data, or - creating two variations for all / most / many string functions: one with
MAX
types, and one without (for when you are guaranteed to never go over 8000 bytes of string data in or out). This is the approach I chose to take for most of the functions in my SQL# library: there is aTrim()
function which likely has one or moreMAX
types, and aTrim4k()
version which never has aMAX
type anywhere in the signature or result set schema. The "4k" versions are absolutely more efficient.
- using only
You are not being careful to emulate functionality given the example in the question.
LTRIM
andRTRIM
only trim spaces, while .NETString.Trim()
trims white space (at least space, tabs, and newlines). For example:
PRINT LTRIM(RTRIM(N' a '));
- Also, I just noticed that your function, both in T-SQL and in C#, only uses 1 of the 3 input parameters. Is this just a proof of concept, or redacted code?
edited 11 hours ago
answered 11 hours ago
Solomon RutzkySolomon Rutzky
49.9k585184
49.9k585184
1. Thanks for the tip on using the Sql types. I'll make that change now. 2. There are external forces at work here which necessitate the use of them. I'm not thrilled about it but trust me, it's better than the alternative. My original question contains a little bit of the explanation as to why a seemingly asinine function exists and is being used.
– Russ Suter
10 hours ago
@RussSuter Understood re: external forces. I was just pointing out some pitfalls that might not have been known when that decision was made. Either way, I'm unable to find my notes or reproduce the scenario from the few details I remember of it. I just remember something definitely changing in 2017 with regards to transactions and calling code from an assembly, and being really annoyed with it as it seemed like a needless change for the worse, and I had to work around it for what I was testing that worked fine in prior versions. So, please post a link in the question to the deadlock XML.
– Solomon Rutzky
10 hours ago
Thanks for that additional info. Here's a link to the XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
– Russ Suter
8 hours ago
@RussSuter Have you tried this with inlining the T-SQL? Looking at the deadlock XML (which is not easy as it's a single line--all newlines got removed somehow) it appears to be a series of PAGE locks between sessions 60 and 78. There are 8 pages locked between both sessions: 3 for one SPID and 5 for the other. Each with a different process ID, so this is an issue of parallelism. If this is related to SQLCLR, it might ironically be the fact that SQLCLR isn't preventing parallelism. This is why I asked if you have tried putting the simple function inline as that might also show the deadlock.
– Solomon Rutzky
8 hours ago
add a comment |
1. Thanks for the tip on using the Sql types. I'll make that change now. 2. There are external forces at work here which necessitate the use of them. I'm not thrilled about it but trust me, it's better than the alternative. My original question contains a little bit of the explanation as to why a seemingly asinine function exists and is being used.
– Russ Suter
10 hours ago
@RussSuter Understood re: external forces. I was just pointing out some pitfalls that might not have been known when that decision was made. Either way, I'm unable to find my notes or reproduce the scenario from the few details I remember of it. I just remember something definitely changing in 2017 with regards to transactions and calling code from an assembly, and being really annoyed with it as it seemed like a needless change for the worse, and I had to work around it for what I was testing that worked fine in prior versions. So, please post a link in the question to the deadlock XML.
– Solomon Rutzky
10 hours ago
Thanks for that additional info. Here's a link to the XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
– Russ Suter
8 hours ago
@RussSuter Have you tried this with inlining the T-SQL? Looking at the deadlock XML (which is not easy as it's a single line--all newlines got removed somehow) it appears to be a series of PAGE locks between sessions 60 and 78. There are 8 pages locked between both sessions: 3 for one SPID and 5 for the other. Each with a different process ID, so this is an issue of parallelism. If this is related to SQLCLR, it might ironically be the fact that SQLCLR isn't preventing parallelism. This is why I asked if you have tried putting the simple function inline as that might also show the deadlock.
– Solomon Rutzky
8 hours ago
1. Thanks for the tip on using the Sql types. I'll make that change now. 2. There are external forces at work here which necessitate the use of them. I'm not thrilled about it but trust me, it's better than the alternative. My original question contains a little bit of the explanation as to why a seemingly asinine function exists and is being used.
– Russ Suter
10 hours ago
1. Thanks for the tip on using the Sql types. I'll make that change now. 2. There are external forces at work here which necessitate the use of them. I'm not thrilled about it but trust me, it's better than the alternative. My original question contains a little bit of the explanation as to why a seemingly asinine function exists and is being used.
– Russ Suter
10 hours ago
@RussSuter Understood re: external forces. I was just pointing out some pitfalls that might not have been known when that decision was made. Either way, I'm unable to find my notes or reproduce the scenario from the few details I remember of it. I just remember something definitely changing in 2017 with regards to transactions and calling code from an assembly, and being really annoyed with it as it seemed like a needless change for the worse, and I had to work around it for what I was testing that worked fine in prior versions. So, please post a link in the question to the deadlock XML.
– Solomon Rutzky
10 hours ago
@RussSuter Understood re: external forces. I was just pointing out some pitfalls that might not have been known when that decision was made. Either way, I'm unable to find my notes or reproduce the scenario from the few details I remember of it. I just remember something definitely changing in 2017 with regards to transactions and calling code from an assembly, and being really annoyed with it as it seemed like a needless change for the worse, and I had to work around it for what I was testing that worked fine in prior versions. So, please post a link in the question to the deadlock XML.
– Solomon Rutzky
10 hours ago
Thanks for that additional info. Here's a link to the XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
– Russ Suter
8 hours ago
Thanks for that additional info. Here's a link to the XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
– Russ Suter
8 hours ago
@RussSuter Have you tried this with inlining the T-SQL? Looking at the deadlock XML (which is not easy as it's a single line--all newlines got removed somehow) it appears to be a series of PAGE locks between sessions 60 and 78. There are 8 pages locked between both sessions: 3 for one SPID and 5 for the other. Each with a different process ID, so this is an issue of parallelism. If this is related to SQLCLR, it might ironically be the fact that SQLCLR isn't preventing parallelism. This is why I asked if you have tried putting the simple function inline as that might also show the deadlock.
– Solomon Rutzky
8 hours ago
@RussSuter Have you tried this with inlining the T-SQL? Looking at the deadlock XML (which is not easy as it's a single line--all newlines got removed somehow) it appears to be a series of PAGE locks between sessions 60 and 78. There are 8 pages locked between both sessions: 3 for one SPID and 5 for the other. Each with a different process ID, so this is an issue of parallelism. If this is related to SQLCLR, it might ironically be the fact that SQLCLR isn't preventing parallelism. This is why I asked if you have tried putting the simple function inline as that might also show the deadlock.
– Solomon Rutzky
8 hours ago
add a comment |
Russ Suter is a new contributor. Be nice, and check out our Code of Conduct.
Russ Suter is a new contributor. Be nice, and check out our Code of Conduct.
Russ Suter is a new contributor. Be nice, and check out our Code of Conduct.
Russ Suter is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Database Administrators Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f236794%2fapparently-my-clr-assembly-function-is-causing-deadlocks%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
9
Deterministic scalar CLR functions should not contribute to deadlocks. Of course CLR functions that read the database might. You should include the deadlock XML in your question.
– David Browne - Microsoft
11 hours ago