PageRenderTime 47ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Kilimanjaro_Trunk/FastDataPush/Scripts/2 - Initiator setup.sql

#
SQL | 409 lines | 278 code | 50 blank | 81 comment | 0 complexity | 50e194abd6bb4d6bb4a48a00f46fbe13 MD5 | raw file
  1. --------------------------------------------------------------------
  2. -- Script for fast data push sample.
  3. --
  4. -- This file is part of the Microsoft SQL Server Code Samples.
  5. -- Copyright (C) Microsoft Corporation. All Rights reserved.
  6. -- This source code is intended only as a supplement to Microsoft
  7. -- Development Tools and/or on-line documentation. See these other
  8. -- materials for detailed information regarding Microsoft code samples.
  9. --
  10. -- THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
  11. -- ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  12. -- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  13. -- PARTICULAR PURPOSE.
  14. --------------------------------------------------------------------
  15. ----------------------------------------------------
  16. -- Initiator setup for fast data push.
  17. -- Before running, customize the configuration-dependent
  18. -- routing to the target service.
  19. ----------------------------------------------------
  20. USE data_push_database;
  21. GO
  22. -- The data push procedure: send messages to target.
  23. CREATE PROCEDURE usp_data_push
  24. AS
  25. BEGIN
  26. SET NOCOUNT ON;
  27. -- Get initiator parameters.
  28. DECLARE @message_quantity BIGINT;
  29. DECLARE @message_size INT;
  30. DECLARE @number_initiator_transactions INT;
  31. DECLARE @initiator_transaction_delay CHAR(12);
  32. DECLARE @number_dialogs INT;
  33. DECLARE @dialog_recycle_max_messages BIGINT;
  34. SET @message_quantity = (SELECT message_quantity FROM data_push_parameters);
  35. SET @message_size = (SELECT message_size FROM data_push_parameters);
  36. SET @number_initiator_transactions = (SELECT number_initiator_transactions FROM data_push_parameters);
  37. SET @initiator_transaction_delay = (SELECT initiator_transaction_delay FROM data_push_parameters);
  38. SET @number_dialogs = (SELECT number_dialogs FROM data_push_parameters);
  39. SET @dialog_recycle_max_messages = (SELECT dialog_recycle_max_messages FROM data_push_parameters);
  40. -- Create a message payload of the requested size.
  41. DECLARE @payload VARCHAR(MAX);
  42. DECLARE @char VARCHAR(MAX);
  43. SET @char = '0';
  44. SELECT @payload = REPLICATE(@char, @message_size);
  45. -- Loop controls.
  46. DECLARE @messages_per_transaction BIGINT;
  47. DECLARE @messages_per_dialog_transaction BIGINT;
  48. DECLARE @transaction_counter INT;
  49. DECLARE @message_counter BIGINT;
  50. -- Compute messages to send per dialog per transaction:
  51. -- @message_quantity / (@number_initiator_transactions x @number_dialogs)
  52. -- Note that integer arithmetic may result in "remainder" messages that will not
  53. -- be sent.
  54. SET @messages_per_transaction = @message_quantity / @number_initiator_transactions;
  55. SET @messages_per_dialog_transaction = @messages_per_transaction / @number_dialogs;
  56. -- Error variables.
  57. DECLARE @error_number INT;
  58. DECLARE @error_message VARCHAR(4000);
  59. -- Show start time.
  60. SELECT GETDATE() AS 'Start sending';
  61. -- Create a table containing requested number of dialogs.
  62. DECLARE @dialogs TABLE (idx INT, handle UNIQUEIDENTIFIER, recycle_counter BIGINT);
  63. DECLARE @idx INT;
  64. DECLARE @handle UNIQUEIDENTIFIER;
  65. DECLARE @recycle_counter BIGINT;
  66. SET @idx = 0;
  67. WHILE @idx < @number_dialogs
  68. BEGIN
  69. BEGIN DIALOG CONVERSATION @handle
  70. FROM SERVICE initiator_service
  71. TO SERVICE 'target_service'
  72. ON CONTRACT data_push_contract
  73. WITH ENCRYPTION = OFF;
  74. INSERT INTO @dialogs (idx, handle, recycle_counter) VALUES (@idx, @handle, 0);
  75. SET @idx = @idx + 1;
  76. END
  77. -- Loop through transactions.
  78. SET @transaction_counter = 0;
  79. WHILE @transaction_counter < @number_initiator_transactions
  80. BEGIN
  81. BEGIN TRANSACTION;
  82. -- Loop through dialogs.
  83. SET @idx = 0;
  84. WHILE @idx < @number_dialogs
  85. BEGIN
  86. -- Send a batch of messages for dialog.
  87. SET @handle = (SELECT handle FROM @dialogs WHERE idx = @idx);
  88. SET @recycle_counter = (SELECT recycle_counter FROM @dialogs WHERE idx = @idx);
  89. SET @message_counter = 0;
  90. WHILE @message_counter < @messages_per_dialog_transaction
  91. BEGIN
  92. -- Time to recycle dialog?
  93. IF @dialog_recycle_max_messages <> -1 AND
  94. @recycle_counter = @dialog_recycle_max_messages
  95. BEGIN
  96. -- Inform target to end dialog.
  97. SEND ON CONVERSATION @handle MESSAGE TYPE end_of_stream;
  98. -- Replace the current dialog.
  99. BEGIN DIALOG CONVERSATION @handle
  100. FROM SERVICE initiator_service
  101. TO SERVICE 'target_service'
  102. ON CONTRACT data_push_contract
  103. WITH ENCRYPTION = OFF;
  104. UPDATE @dialogs SET handle = @handle WHERE idx = @idx;
  105. SET @recycle_counter = 0;
  106. END
  107. -- Send a message.
  108. BEGIN TRY
  109. BEGIN
  110. SEND ON CONVERSATION @handle MESSAGE TYPE data_push_message (@payload);
  111. END
  112. IF @dialog_recycle_max_messages <> -1
  113. BEGIN
  114. SET @recycle_counter = @recycle_counter + 1;
  115. END
  116. SET @message_counter = @message_counter + 1;
  117. END TRY
  118. BEGIN CATCH
  119. SET @error_number = ERROR_NUMBER();
  120. SET @error_message = ERROR_MESSAGE();
  121. -- Dialog is faulty?
  122. DECLARE @dialog_error INT;
  123. SET @dialog_error = 1;
  124. DECLARE @dialog_state VARCHAR(2);
  125. SET @dialog_state = (SELECT state FROM sys.conversation_endpoints
  126. WHERE conversation_handle = @handle);
  127. IF @@ROWCOUNT = 1
  128. BEGIN
  129. -- Good dialog is starting or conversing.
  130. IF @dialog_state = 'SO' OR @dialog_state = 'CO'
  131. BEGIN
  132. SET @dialog_error = 0;
  133. END
  134. END
  135. IF @dialog_error = 1
  136. BEGIN
  137. -- Record the error.
  138. INSERT INTO initiator_processing_errors VALUES(@handle, @error_number,
  139. @error_message, NULL, NULL, NULL, NULL, 0);
  140. -- Replace dialog and continue sending.
  141. BEGIN DIALOG CONVERSATION @handle
  142. FROM SERVICE initiator_service
  143. TO SERVICE 'target_service'
  144. ON CONTRACT data_push_contract
  145. WITH ENCRYPTION = OFF;
  146. UPDATE @dialogs SET handle = @handle WHERE idx = @idx;
  147. SET @recycle_counter = 0;
  148. END
  149. ELSE
  150. BEGIN
  151. -- Record the error and return error.
  152. INSERT INTO initiator_processing_errors VALUES(@handle, @error_number,
  153. @error_message, NULL, NULL, NULL, NULL, 0);
  154. RETURN 1;
  155. END
  156. END CATCH
  157. END
  158. UPDATE @dialogs SET recycle_counter = @recycle_counter WHERE idx = @idx;
  159. SET @idx = @idx + 1;
  160. END
  161. COMMIT;
  162. SET @transaction_counter = @transaction_counter + 1;
  163. -- Wait for next transaction.
  164. IF @transaction_counter < @number_initiator_transactions
  165. BEGIN
  166. WAITFOR DELAY @initiator_transaction_delay;
  167. END
  168. END
  169. -- Gracefully end dialogs by informing target.
  170. BEGIN TRANSACTION;
  171. SET @idx = 0;
  172. WHILE @idx < @number_dialogs
  173. BEGIN
  174. SET @handle = (SELECT handle FROM @dialogs WHERE idx = @idx);
  175. BEGIN
  176. SEND ON CONVERSATION @handle MESSAGE TYPE end_of_stream;
  177. END
  178. SET @idx = @idx + 1;
  179. END
  180. COMMIT;
  181. -- Show end time.
  182. SELECT GETDATE() AS 'End sending';
  183. RETURN 0;
  184. END;
  185. GO
  186. -- Resends all pending messages in sys.transmission_queue
  187. -- belonging to an old conversation on a new conversation.
  188. CREATE PROCEDURE usp_resend_pending (@old_handle UNIQUEIDENTIFIER)
  189. AS
  190. BEGIN
  191. SET NOCOUNT ON;
  192. DECLARE @message_type_name SYSNAME;
  193. DECLARE @message_body VARCHAR(MAX);
  194. -- Get a new dialog.
  195. DECLARE @handle UNIQUEIDENTIFIER;
  196. BEGIN DIALOG CONVERSATION @handle
  197. FROM SERVICE initiator_service
  198. TO SERVICE 'target_service'
  199. ON CONTRACT data_push_contract
  200. WITH ENCRYPTION = OFF;
  201. -- Declare a cursor to iterate over all the pending messages.
  202. -- It is important to keep the message order and to keep the original message type.
  203. DECLARE cursor_pending CURSOR LOCAL FORWARD_ONLY READ_ONLY
  204. FOR SELECT message_type_name, message_body
  205. FROM sys.transmission_queue
  206. WHERE conversation_handle = @old_handle
  207. ORDER BY message_sequence_number;
  208. OPEN cursorPending;
  209. FETCH NEXT FROM cursor_pending INTO @message_type_name, @message_body;
  210. WHILE (@@FETCH_STATUS = 0)
  211. BEGIN
  212. -- Resend the message on the new conversation
  213. SEND ON CONVERSATION @handle MESSAGE TYPE @message_type_name (@message_body);
  214. FETCH NEXT FROM cursor_pending INTO @message_type_name, @message_body;
  215. END
  216. CLOSE cursor_pending;
  217. DEALLOCATE cursor_pending;
  218. -- Signal end of stream to target.
  219. SEND ON CONVERSATION @handle MESSAGE TYPE end_of_stream;
  220. END;
  221. GO
  222. -- Activated store proc for the initiator to receive messages.
  223. -- Dialogs are gracefully ended by the target after receiving
  224. -- an end_of_stream message from the initiator; the end dialog
  225. -- message is then processed here. This method is recommended
  226. -- to avoid "fire and forget" message loss. One message per
  227. -- invocation is OK here for expected low-volume load.
  228. CREATE PROCEDURE initiator_queue_activated_procedure
  229. AS
  230. BEGIN
  231. SET NOCOUNT ON;
  232. DECLARE @conversation_handle UNIQUEIDENTIFIER,
  233. @message_type_name SYSNAME,
  234. @message_body VARCHAR(MAX);
  235. -- Error variables.
  236. DECLARE @error_number INT;
  237. DECLARE @error_message VARCHAR(4000);
  238. DECLARE @error_severity INT;
  239. DECLARE @error_state INT;
  240. DECLARE @error_procedure SYSNAME;
  241. DECLARE @error_line INT;
  242. BEGIN TRY
  243. BEGIN TRANSACTION;
  244. -- Wait 5 seconds for a message.
  245. WAITFOR (
  246. RECEIVE TOP(1)
  247. @conversation_handle = conversation_handle,
  248. @message_type_name = message_type_name,
  249. @message_body = message_body
  250. FROM initiator_queue), TIMEOUT 5000;
  251. IF @@ROWCOUNT = 1
  252. BEGIN
  253. IF @message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
  254. BEGIN
  255. -- Target is ending dialog normally.
  256. END CONVERSATION @conversation_handle;
  257. END
  258. ELSE IF @message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
  259. BEGIN
  260. -- Record the error.
  261. WITH XMLNAMESPACES ('http://schemas.microsoft.com/SQL/ServiceBroker/Error' AS ssb)
  262. SELECT
  263. @error_number = CAST(@message_body AS XML).value('(//ssb:Error/ssb:Code)[1]', 'INT'),
  264. @error_message = CAST(@message_body AS XML).value('(//ssb:Error/ssb:Description)[1]', 'VARCHAR(4000)');
  265. INSERT INTO initiator_processing_errors VALUES(@conversation_handle, @error_number,
  266. @error_message, NULL, NULL, NULL, NULL, 0);
  267. -- Can messages be resent?
  268. IF (@error_number IN (-8489, -8462, -9719, -28052))
  269. BEGIN
  270. -- Resend the messages on a new dialog.
  271. EXEC usp_resend_pending @conversation_handle;
  272. END
  273. ELSE
  274. BEGIN
  275. -- Save the messages in a side table to be processed later.
  276. INSERT INTO unsent_messages
  277. SELECT message_type_name, message_body FROM sys.transmission_queue
  278. WHERE conversation_handle = @conversation_handle;
  279. END
  280. -- End the conversation.
  281. END CONVERSATION @conversation_handle;
  282. END
  283. END
  284. COMMIT;
  285. END TRY
  286. BEGIN CATCH
  287. SET @error_number = ERROR_NUMBER();
  288. SET @error_message = ERROR_MESSAGE();
  289. SET @error_severity = ERROR_SEVERITY();
  290. SET @error_state = ERROR_STATE();
  291. SET @error_procedure = ERROR_PROCEDURE();
  292. SET @error_line = ERROR_LINE();
  293. IF XACT_STATE() = -1
  294. BEGIN
  295. -- The transaction is doomed. Only rollback possible.
  296. -- Note: 5 consecutive rollbacks will disable the queue!
  297. ROLLBACK TRANSACTION;
  298. -- Record the error.
  299. BEGIN TRANSACTION;
  300. INSERT INTO initiator_processing_errors VALUES(NULL, @error_number, @error_message,
  301. @error_severity, @error_state, @error_procedure, @error_line, 1);
  302. COMMIT;
  303. END
  304. ELSE IF XACT_STATE() = 1
  305. BEGIN
  306. -- Record error and commit transaction.
  307. INSERT INTO initiator_processing_errors VALUES(NULL, @error_number, @error_message,
  308. @error_severity, @error_state, @error_procedure, @error_line, 0);
  309. COMMIT;
  310. END
  311. END CATCH
  312. END;
  313. GO
  314. -- Create the initiator queue with activated procedure.
  315. CREATE QUEUE initiator_queue
  316. WITH ACTIVATION (
  317. STATUS = ON,
  318. MAX_QUEUE_READERS = 1,
  319. PROCEDURE_NAME = initiator_queue_activated_procedure,
  320. EXECUTE AS OWNER);
  321. GO
  322. -- Create initiator service.
  323. CREATE SERVICE initiator_service ON QUEUE initiator_queue (data_push_contract);
  324. GO
  325. -- Any user can send on the service.
  326. GRANT SEND ON SERVICE::initiator_service TO PUBLIC;
  327. GO
  328. -- This table stores unsent messages.
  329. IF EXISTS (SELECT name FROM sys.tables WHERE name = 'unsent_messages')
  330. DROP TABLE unsent_messages;
  331. GO
  332. CREATE TABLE unsent_messages ( message_type_name SYSNAME, message_body VARCHAR(MAX) );
  333. GO
  334. -- Table to store processing errors.
  335. IF EXISTS (SELECT name FROM sys.tables WHERE name = 'initiator_processing_errors')
  336. DROP TABLE initiator_processing_errors;
  337. GO
  338. CREATE TABLE initiator_processing_errors (error_conversation UNIQUEIDENTIFIER, error_number INT,
  339. error_message VARCHAR(4000), error_severity INT, error_state INT, error_procedure SYSNAME NULL,
  340. error_line INT, doomed_transaction TINYINT)
  341. GO
  342. ---------------------------------------------------------------------
  343. -- Routing.
  344. -- Skip the following if services are in the same database instance.
  345. ---------------------------------------------------------------------
  346. -- Create a route to the target service.
  347. CREATE ROUTE target_route
  348. WITH SERVICE_NAME = 'target_service',
  349. ADDRESS = 'tcp://target_host:4022';
  350. GO
  351. -- In msdb, create an incoming route to the initiator service.
  352. USE msdb;
  353. GO
  354. CREATE ROUTE initiator_route
  355. WITH SERVICE_NAME = 'initiator_service',
  356. ADDRESS = 'local';
  357. GO
  358. USE data_push_database;
  359. GO