Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / И снова про автономные транзакции с помощью CLR / 21 сообщений из 21, страница 1 из 1
28.02.2015, 16:45
    #38891372
Stibrus
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
Добрый день! Возникла потребность логгирования процесса обработки данных на SQL Server 2005 в автономной транзакции. Интернет предлагает 3 стандартных обходных маневра - с loopback linked server'ом, табличной переменной и написанием хранимой процедуры на CLR. Т.к. первые 2 варианта мне по тем или иным причинам не нравятся (покупной продукт, клиентское приложение - "черный ящик", требования к производительности и надежности), попробовал сделать хранимую процедуру на CLR. Забегая вперед, скажу, что несмотря на все рецепты и уверения Микрософт на МСДН, автономное соединение из CLR подхватывает "внешнюю" транзакцию!

Создаем таблицу

CREATE TABLE [dbo].[EventLog](
[ID] uniqueidentifier default NEWID() not null primary key,
[EventBody] varchar(max)
) ON [PRIMARY]
GO


Создаем хранимую процедуру CLR

public static class Logger
{
private const string cmdWriteLog =
"INSERT INTO [dbo].[EventLog] ([EventBody]) VALUES (@EventBody)";

private const string unspecified = "<UNSPECIFIED>";

[SqlFunction]
public static void LogWrite(string pEventBody)
{
try
{
using (var transaction = new TransactionScope(TransactionScopeOption.RequiresNew))
{
using (var connection = new SqlConnection("context connection=true"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = cmdWriteLog;

command.Parameters.Add("@EventBody", System.Data.SqlDbType.Text);
if (pEventBody == null || string.IsNullOrEmpty(pEventBody))
command.Parameters["@EventBody"].Value = string.Empty;
else
command.Parameters["@EventBody"].Value = pEventBody;

command.ExecuteNonQuery();
transaction.Complete();
}
} // Connection
} // Transaction scope
}
catch (Exception) { }
}
}


Прописываем нашу сборку на сервере

sp_configure 'clr enabled', 1
RECONFIGURE

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[LogWriteInternal]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[LogWriteInternal]


IF EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'SqlServerFunctions' and is_user_defined = 1)
DROP ASSEMBLY [SqlServerFunctions]


CREATE ASSEMBLY SqlServerFunctions FROM 'c:\Test\SqlServerFunctions.dll' WITH PERMISSION_SET = SAFE

CREATE PROCEDURE [dbo].[LogWriteInternal]
(
@pEventBody nvarchar(max)
)
AS EXTERNAL NAME SqlServerFunctions.Logger.LogWrite


И, наконец, тестируемся. Если вызываем процедуру без явного объявления транзакции, сообщение пишется в лог. Если делаем явное объявление/откат транзакции, сообщение не пишется!

BEGIN TRAN

DECLARE
@pEventBody nvarchar(max)
SET @pEventBody = 'This is an event body'
EXEC [dbo].[LogWriteInternal] @pEventBody

ROLLBACK TRAN

Вопрос: то ли Микрософт обманывает, и функция CLR получает соединение, из которого происходит вызов (вместо открытия нового соединения), то ли TransactionScopeOption.RequiresNew не создает автономную транзакцию, а подключает меня к "уже имеющейся".

Кто-нибудь сумел победить это, и сумел сделать автономную транзакцию с помощью CLR? Поможите, люди добрые!
...
Рейтинг: 0 / 0
28.02.2015, 17:33
    #38891386
churupaha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
Stibrus,

да вроде все логично

авторIf you need to connect to the same server on which the stored procedure or function is running, use the context connection in most cases. This has benefits such as
Код: sql
1.
running in the same transaction space

and not having to reauthenticate.

вот тут костыль через CLR описан , идея в том что они Context Connection используют только для получения инфы о сервере, а затем устанавливают Regular Connection на основе полученной инфы. И устанавливают Enlist = false, чтобы не участвовать в ambient транзакции. Но оно вам надо? может лучше через linked server?
...
Рейтинг: 0 / 0
28.02.2015, 17:38
    #38891387
churupaha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
churupaha,

в статье поиском найти "Simulating Autonomous Transactions"... а то статья о другом трохи.
...
Рейтинг: 0 / 0
28.02.2015, 20:50
    #38891460
Stibrus
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
churupaha вот тут костыль через CLR описан , идея в том что они Context Connection используют только для получения инфы о сервере, а затем устанавливают Regular Connection на основе полученной инфы. И устанавливают Enlist = false, чтобы не участвовать в ambient транзакции. Но оно вам надо? может лучше через linked server?

Благодарю! Не могу найти ссылку на МСДНе, которая сбила меня с толку, но это случилось :) Я подумаю над Вашим советом с linked server, т.к. описанный по Вашей ссылке метод, кажется, подразумевает много неприятностей (ALTER DATABASE .... SET TRUSTWORTHY ON, установку сборки в режиме UNSAFE).
...
Рейтинг: 0 / 0
28.02.2015, 21:31
    #38891471
invm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
event notification для userconfigurable_* + sp_trace_generateevent
15757563
...
Рейтинг: 0 / 0
28.02.2015, 21:57
    #38891481
churupaha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
Stibrus(ALTER DATABASE .... SET TRUSTWORTHY ON, установку сборки в режиме UNSAFE).

Я ни в коем случае вас не агитирую в конкретно этом случае использовать CLR, наоборот.

Просто прокомментирую выделенное по поводу развертывания UNSAFE сборки. Правильно так:

1) подписуете сборку ключем
2) на стороне sql server

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
use master;
go

create assymmetric key __my_clr_assembly_key from executable file = N'c:\temp\my_clr_assembly.dll'
go

create login __my_clr_assembly_login from asymmetric key __my_clr_assembly_key;
go


grant unsafe assembly to __my_clr_assembly_login;
go

/*
     если у вас external access assembly

     grant external access to __my_clr_assembly_login;
*/

use db1
go

create assembly my_clr_assembly from N'c:\temp\my_clr_assembly.dll' with permission_set = unsafe /* external_access */
...
Рейтинг: 0 / 0
28.02.2015, 21:59
    #38891482
churupaha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
+

Код: sql
1.
/* !!!!! */ alter database db1 set trustworthy OFF /* !!!!! */ 
...
Рейтинг: 0 / 0
01.03.2015, 00:37
    #38891537
Stibrus
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
invmevent notification для userconfigurable_* + sp_trace_generateevent
15757563



invmevent notification для userconfigurable_* + sp_trace_generateevent
15757563

Благодарю, но в моем случае загвоздка в том, что управляющее приложение - коробочный продукт, я не могу "протолкнуть информацию наверх", чтобы там ее обработать.

churupahaЯ ни в коем случае вас не агитирую в конкретно этом случае использовать CLR, наоборот.

Еще раз спасибо, всё заработало "на 5 баллов". Насчет выбора между loopback и CLR - поставлю вопрос перед DBA на работе, посмотрим, что ответят. Есть один момент, который вызывает у меня сомнение: как себя поведет linked server loopback в распределенной транзакции, если, например, будет вовлечен Оракл?
...
Рейтинг: 0 / 0
01.03.2015, 00:39
    #38891538
Stibrus
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
Забыл сказать: у меня всё заработало с UNSAFE, хотя кое-где почему-то пишут, что понадобится EXTERNAL_ACCESS
...
Рейтинг: 0 / 0
01.03.2015, 09:53
    #38891572
churupaha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
StibrusЕсть один момент, который вызывает у меня сомнение: как себя поведет linked server loopback в распределенной транзакции, если, например, будет вовлечен Оракл?


remote proc transaction promotion

msdnIf this option is set to FALSE (or OFF), a local transaction will not be promoted to a distributed transaction while calling a remote procedure call on a linked server.

StibrusЗабыл сказать: у меня всё заработало с UNSAFE, хотя кое-где почему-то пишут, что понадобится EXTERNAL_ACCESS


Заработает и это. Просто сделаете:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
use master;
go

...

grant external access assembly to __my_clr_assembly_login;
go

use db1
go

create assembly my_clr_assembly from N'c:\temp\my_clr_assembly.dll' with permission_set = external_access
...
Рейтинг: 0 / 0
01.03.2015, 10:19
    #38891579
invm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
Stibrusя не могу "протолкнуть информацию наверх", чтобы там ее обработать.Не нужно ничего "проталкивать". Все работает на уровне сиквела.
...
Рейтинг: 0 / 0
01.03.2015, 10:41
    #38891584
Stibrus
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
invmStibrusя не могу "протолкнуть информацию наверх", чтобы там ее обработать.Не нужно ничего "проталкивать". Все работает на уровне сиквела.Если речь про Service Broker, то надо обдумать. Как-то не смотрел в его сторону.

З.Ы. Когда заводил вопрос, не нашел поиском тему по вашей ссылке, наверное, из-за названия.
...
Рейтинг: 0 / 0
01.03.2015, 11:57
    #38891612
Stibrus
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
invmevent notification для userconfigurable_* + sp_trace_generateevent
15757563
Объясню, чем мне не подходит sp_trace_generateevent: мне нужна информация не в реалтайме (в профайлере), а "задним числом", и притом структурированная. То, что прекрасно подходит для целей ДБА, не покроет потребностей бизнес-пользователя.

Т.е. мне нужно отделять
- Дату и время события
- Уровень события (ERROR/INFO/WARNING.....)
- Источник события (хранимая процедура)
- Инициатор события : вызвавшая рабочая станция
- Инициатор события : вызвавший пользователь
- Инициатор события : вызвавшее приложение
- По возможности: идентификатор бизнес-объекта
- По возможности: тип бизнес-объекта
- И только потом идет детальная информация о том, что случилось, в виде свободного текста.

Типовые сценарии:
- импортированный ранее из системы (А) документ (ID) при передаче в систему (B) выдал ошибку
- просят посмотреть, почему месяц назад джоб поменял атрибут документа (ID): не вносил ли кто-то изменения, которые могли "спровоцировать" джоб
- наконец, типовой сценарий: кто и когда менял документ (ID)

Доставать такие данные из нетипизированных MEMO-полей, даже если там XML - очень долго и ненадежно в плане достоверности.
...
Рейтинг: 0 / 0
01.03.2015, 12:37
    #38891641
churupaha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
StibrusДоставать такие данные из нетипизированных MEMO-полей , ..., очень долго и ненадежно в плане достоверности.

Комментарий относится только к выделенному (не к задаче).

можно быстро и удобно
...
Рейтинг: 0 / 0
02.03.2015, 01:07
    #38891970
invm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
StibrusОбъясню, чем мне не подходит sp_trace_generateevent: мне нужна информация не в реалтайме (в профайлере), а "задним числом", и притом структурированная. То, что прекрасно подходит для целей ДБА, не покроет потребностей бизнес-пользователя.StibrusДоставать такие данные из нетипизированных MEMO-полей, даже если там XML - очень долго и ненадежно в плане достоверности.Какой реалтайм? Какой профайлер? Какие MEMO-поля? Вы вообще о чем?
Хоть пробовали читать, что такое event notification?

Вот вам болванка для дальнейших упражнений:
Код: sql
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.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
use master;

if db_id('DBAuditExample') is not null
begin
 alter database DBAuditExample set single_user with rollback immediate;
 drop database DBAuditExample;
end;

create database DBAuditExample;
alter database DBAuditExample set recovery simple;
alter database DBAuditExample set enable_broker with rollback immediate;
go

use DBAuditExample;
go

if exists(select * from sys.server_event_notifications where name = 'enSimpleAudit')
 drop event notification enSimpleAudit on server;
go

if object_id('dbo.spHandleMessages', 'P') is null
 exec('create procedure dbo.spHandleMessages as begin set nocount on; end;');
go

create queue dbo.qSimpleAudit with
 status = on,
 retention = off,
 activation (status = on, procedure_name = dbo.spHandleMessages, max_queue_readers = 5, execute as owner),
 poison_message_handling (status = on);
go

create service svcSimpleAudit
 authorization dbo
 on queue dbo.qSimpleAudit
 (
  [http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]
 );
go

create table dbo.SimpleAuditData
(
 ad_id int identity,
 ad_OccurredAt datetime not null,
 ad_Login sysname,
 ad_Application nvarchar(128),
 ad_Host nvarchar(128),
 ad_ShortText nvarchar(128),
 ad_LongText nvarchar(4000),
 constraint PK_SimpleAuditData primary key (ad_id)
);
go

alter procedure dbo.spHandleMessages
as
begin
 set nocount on;

 declare @handle uniqueidentifier, @message xml, @message_type sysname;

 waitfor
 (
  receive top (1)
   @handle = conversation_handle,
   @message_type = message_type_name,
   @message = message_body
  from
   dbo.qSimpleAudit
 ),
 timeout 1000;

 if @@rowcount = 0
  return 0;

 if @message_type = N'http://schemas.microsoft.com/SQL/Notifications/EventNotification'
  with s as
  (
   select
    n.value('EventType[1]', 'sysname') as EventType,
    n.value('PostTime[1]', 'datetime') as PostTime,
    n.value('TextData[1]', 'sysname') as TextData,
    n.value('BinaryData[1]', 'varbinary(8000)') as BinaryData,
    n.value('DatabaseID[1]', 'int') as DatabaseID,
    n.value('DatabaseName[1]', 'sysname') as DatabaseName,
    n.value('ApplicationName[1]', 'sysname') as ApplicationName,
    n.value('HostName[1]', 'sysname') as HostName,
    n.value('SessionLoginName[1]', 'sysname') as SessionLoginName
   from
    @message.nodes('/EVENT_INSTANCE') x(n)
  )
  insert into dbo.SimpleAuditData
   (ad_OccurredAt, ad_Login, ad_Application, ad_Host, ad_ShortText, ad_LongText)
   select
    s.PostTime, s.SessionLoginName, s.ApplicationName, s.HostName, s.TextData, cast(s.BinaryData as nvarchar(4000))
   from
    s
   where
    DatabaseID = db_id();

 if @message_type = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
  end conversation @handle with cleanup;

 if @message_type = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
  end conversation @handle with cleanup;

end;
go

create event notification enSimpleAudit
on server
for USERCONFIGURABLE_0
to service 'svcSimpleAudit', 'current database';
go


Использование:
Код: sql
1.
2.
declare @b varbinary(8000) = cast(N'Long text' as varbinary(8000));
exec sp_trace_generateevent 82, N'Short text', @b;
...
Рейтинг: 0 / 0
02.03.2015, 22:02
    #38892917
Stibrus
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
invm,

Благодарю. Есть подозрение, что для моих целей это "из пушки по воробьям", но с темой обязательно ознакомлюсь. Пригодится если не для текущего проекта, то для чего-то еще.
...
Рейтинг: 0 / 0
03.03.2015, 11:37
    #38893284
Владислав Колосов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
Создал триггер по указанной технологии. Однако, получаю странные записи.
пример триггера:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
ALTER TRIGGER [dbo].[Hist]
ON [dbo].[R1]
AFTER INSERT,DELETE,UPDATE
BEGIN
SET NOCOUNT ON
if not exists (select * from inserted)
begin
	set @b = cast( (select id from deleted for xml raw, type) as varbinary(8000));
	exec tarif.dbo.sp_trace_generateevent 82, N'r1_delete', @b;
END
if not exists (select * from deleted)
begin
	set @c = cast( (select id  from inserted for xml raw, type) as varbinary(8000));
	exec tarif.dbo.sp_trace_generateevent 82, N'r1_create', @c;
end
else
begin
	set @b = cast( (select id from deleted for xml raw, type) as varbinary(8000));
	set @c = cast( (select id from inserted for xml raw, type) as varbinary(8000));
	exec tarif.dbo.sp_trace_generateevent 82, N'r1_udate_delete', @b;
	exec tarif.dbo.sp_trace_generateevent 82, N'r1_udate_insert', @c;
end


В результате я получаю XML для всех событий, кроме N'r1_delete', для которых у меня пустая строка почти всегда. Однако, когда я удаляю запись из SSMS (админ), то вижу полноценный XML. Возникает вопрос - почему для пользователей пусто, срабатывает триггер, но not exists (select * from inserted) понимается неверно?
...
Рейтинг: 0 / 0
03.03.2015, 11:53
    #38893322
invm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
Владислав Колосов,

Если бы not exists (select * from inserted) интерпретировалось неверно, вы бы вообще не получали r1_delete.
Посмотрите профайлером, что реально приходит в данных события.
...
Рейтинг: 0 / 0
03.03.2015, 13:12
    #38893447
Владислав Колосов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
Разбил на три триггера.
Похоже, что MERGE активирует триггеры INSERT и UPDATE при вставке.
...
Рейтинг: 0 / 0
03.03.2015, 14:13
    #38893552
invm
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
Владислав КолосовПохоже, что MERGE активирует триггеры INSERT и UPDATE при вставке.Это документировано: https://technet.microsoft.com/en-us/library/bb510625(v=sql.105).aspx Trigger Implementation

For every insert, update, or delete action specified in the MERGE statement, SQL Server fires any corresponding AFTER triggers defined on the target table

Если у вас триггер на insert, update, delete и в merge есть ветки insert, update и delete, то триггер будет вызван трижды.
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
24.10.2019, 14:50
    #39880997
DbDude
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
И снова про автономные транзакции с помощью CLR
invm,

А есть ли аналог для Extended Events?
...
Рейтинг: 0 / 0
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / И снова про автономные транзакции с помощью CLR / 21 сообщений из 21, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]