一种包依赖和可重启性的方法,@billinkc
我赞成一种非常精简的执行框架方法——它是一个两表系统,但已经足够了。在每个包的开头,都会触发一个执行 SQL 任务来启动日志记录。当 SSIS 包成功完成后,最后一步是关闭进程的审核行 - 另一个执行 SQL 任务。
由于没有关闭时间以及 SSIS 没有正确运行现在的事实,我知道包异常结束。也许它抛出了外键违规,也许网络掉线了。我真的不喜欢这张桌子。唯一的功能是完成/未完成。如果它没有完成,那么当我们重新启动它时,它就会成为处理的候选对象。
你说的处理候选人?是的,工人包是愚蠢的——他们只做我上面概述的事情。 Orchestrator/parent/master 包有责任担心哪些包需要运行以及以什么顺序运行。如果子包开始运行,那是因为它被告知要运行。它不“知道”它是否已经完成了它的工作。
包的执行历史都在一个表中完成。它不是替代 SSIS 将记录到的 sysdtslog90/sysssislog/catalog.operation_messages 表。根据您的版本,这些表格将包含有关软件包启动但未完成的实际详细信息。
要让master知道什么没跑,它需要知道三件事
- 刷新间隔是多少
- 应该运行哪些进程
- 已经成功运行的内容。
刷新间隔 - 您的业务需求和/或数据可用性决定了这一点,但我认为它需要每天运行一次。魔鬼在细节中,你如何定义那一天——从午夜开始,或者在过去的 24 小时内,或者其他一些规则。不管是什么,您都需要知道在部分重试变成中止并执行完整运行之前多长时间。
需要运行的内容 - 至少,这是父级将调用的所有子包的列表。我喜欢做的事情听起来与您的方法相似,我并行运行许多包,但在该并行块内串行执行。我在那里得到了很好的并行化,而不会淹没盒子。
什么已经成功运行——你已经知道答案了!我们会查看任何已开始运行但没有结束日期的包的包执行历史记录。这些记录中的任何一个都需要重新启动(减少刷新间隔所需的摆弄)。
这是高级概念,希望不是 tl;dr
实施
选择一个数据库,任何数据库来保存这些信息。在某些地方我会创建一个专用的 SSISAudit 数据库,有时我会将其与其他元数据存储库相结合。
这将在您的数据库中创建 3 个对象(不包括表的插入/更新/删除方法)。这两个表和一个存储过程确定需要运行哪些包。将主程序包命名为您需要为其工作的容器,然后输出需要工作的程序包记录集(也就是自午夜以来未成功运行)。
-- This is my package execution history table
CREATE TABLE
dbo.PackageHistory
(
PackageName sysname NOT NULL
, PackageID uniqueidentifier NOT NULL
, ParentPackageID uniqueidentifier NULL
, ExecutionID bigint NULL
, StartTime datetime NOT NULL
, StopTime datetime NULL
, Duration_s AS (DATEDIFF(SECOND, StartTime, StopTime))
);
CREATE TABLE dbo.PackageDependency
(
MasterPackageName sysname NOT NULL
, ContainerName sysname NOT NULL
, ChildPackageName sysname NOT NULL
, ExecutionOrderSequence int NOT NULL
, CONSTRAINT PK_PackageDependency PRIMARY KEY
CLUSTERED
(
MasterPackageName ASC
, ContainerName ASC
, ChildPackageName ASC
)
);
GO
-----------------------------------------------------------------------------
-- Function: AUD.PackageProcesssingControlListGet
-- Author: Bill Fellows
-- Date: 2016-01-18
--
-- This procedure will return a list of outstanding pacakges that need to
-- be processed for a given container. Assumes that processing is done daily
--
-- Recordsets:
-- 0 to N rows will be returned to the caller
--
-- Side-effects:
-- None
--
-- See also:
--
-- Modified:
--
-----------------------------------------------------------------------------
CREATE PROCEDURE dbo.PackageProcesssingControlListGet
(
@MasterPackageName sysname
, @ContainerName sysname
)
AS
BEGIN
SET NOCOUNT ON;
SELECT
PD.ChildPackageName
FROM
dbo.PackageDependency AS PD
LEFT OUTER JOIN
-- Find packages that have not completed
AUD.PackageControl AS PC
ON PC.PackageName = PD.ChildPackageName
-- Since midnight
AND PC.StartTime > CAST(CURRENT_TIMESTAMP AS date)
-- StopTime is only populated when package completes successfully
AND PC.StopTime IS NOT NULL
WHERE
PD.MasterPackageName = @MasterPackageName
AND PD.ContainerName = @ContainerName
AND PC.PackageName IS NULL
ORDER BY
PD.ExecutionOrderSequence ASC;
END
GO
现在呢?让我们举一个简单的例子
这是主包的外观。原谅上面没有注释,我没有安装 Snagit,我对 mspaint 很烂
SQL 开始审计
这是对 PackageHistory 表的插入。我全面指定系统变量,没有为 ParentPackageID 或 StopTime 指定值。 ExecutionID 将是来自System::ServerExecutionID 的值,我认为其余部分是不言而喻的。
SEQC 工作 X
这是一个序列容器。尽可能多地制作这些,以使事情并行运行。 但是,只有在你完成第一个之后才能复制和粘贴,以使你的生活更轻松。
请注意,我定义了 3 个变量,它们的作用域是序列容器。默认情况下,在 2012+ 中,变量是在包级别范围内创建的。在 2005/2008 年,变量是在鼠标焦点所在的位置创建的。对于 2012+,正常创建变量,然后单击变量列表中的第二个图标,即“移动变量”。将这三个移到序列容器中。
这将允许我们使用范围规则来保持变量对其他容器隐藏。并且变得懒惰 - 我是懒惰的粉丝
3 个变量。
- ContainerName - 这是您将存储在 PackageDependency 表的
ContainerName 列中的值。
- CurrentPackageName - 这是当前项目中有效 SSIS 包的名称。没关系,因为它会被覆盖,但有 .dtsx 扩展名
- rsWorkList - 这是一个 Object 类型的变量。它将保存我们存储过程调用的结果。
SQL 获取工作列表
我们需要得到这个容器应该运行的所有包的列表。记住我们创建的存储过程,让我们使用它。 SQL 语句是,你需要指定执行 SQL 任务将返回一个完整的结果集。
EXECUTE dbo.PackageProcesssingControlListGet ?, ?;
在结果集选项卡中,将名称 0 映射到 User::rwWorkList
FELC 粉碎工作清单
这是一个标准的shredding of a recordset。我们在上一步中填充了变量,所以让我们枚举结果。我们的结果集中将有一列,您将其映射到 User::CurrentPackageName
EPT 运行包
序列的最后一步是实际运行我们刚刚从工作列表中弹出的包,因此执行包任务具有基于变量 @[User::CurrentPackageName] 的 PackageName 表达式。
此外,我将使用 Parameter Bindings 选项卡 (2012+) 将当前 System::ExecutionID 作为参数 ParentExecutionID 传递给子包。
SQL 结束审计
如果我们完成此任务,我们将更新 ExecutionID 和 PackageID 匹配且 StopTime 为 NULL 的 PackageHistory 行。
子包
这实际上只是父包,除了中间没有 0 到 N 个序列容器之外,我们只是做子包所做的任何事情。我确实有一个包级别参数来接受父包 ID,但您不必这样做。
看起来很合理?如果是这样,那么您所要做的就是在 dbo.PackageDependency 表上做一些簿记。
INSERT INTO
dbo.PackageDependency
( MasterPackageName
, ContainerName
, ChildPackageName
, ExecutionOrderSequence
)
SELECT
*
FROM
(
VALUES
(
'so_34866238.dtsx'
, 'List0'
, 'C1.dtsx'
, 10
)
,
(
'so_34866238.dtsx'
, 'List0'
, 'C2.dtsx'
, 20
)
,
(
'so_34866238.dtsx'
, 'List1'
, 'D1.dtsx'
, 10
)
) D (MasterPackageName, ContainerName, ChildPackageName, ExecutionOrderSequence);
GO
-- Test we get expected results
-- 2 rows
EXECUTE dbo.PackageProcesssingControlListGet 'so_34866238.dtsx', 'List0';
-- 1 row
EXECUTE dbo.PackageProcesssingControlListGet 'so_34866238.dtsx', 'List1';
-- NULL rows
EXECUTE dbo.PackageProcesssingControlListGet 'so_34866238.dtsx', 'List2';
最后,虽然这还不错,但有点乏味。如果我要这样做,我会用一些 Biml 自动化日光。今晚我写的太晚了,但我会在我分心之前完成它。