Menambahkan Dukungan untuk Penelusuran Kesalahan dalam Tugas Kustom
Berlaku untuk: SQL Server SSIS Integration Runtime di Azure Data Factory
Mesin run-time Integration Services memungkinkan paket, tugas, dan jenis kontainer lainnya ditangguhkan selama eksekusi dengan menggunakan titik henti. Penggunaan titik henti memungkinkan Anda meninjau dan memperbaiki kesalahan yang mencegah aplikasi atau tugas Anda berjalan dengan benar. Arsitektur titik henti memungkinkan klien untuk mengevaluasi nilai run-time objek dalam paket pada titik eksekusi yang ditentukan saat pemrosesan tugas ditangguhkan.
Pengembang tugas kustom dapat menggunakan arsitektur ini untuk membuat target titik henti kustom dengan menggunakan IDTSBreakpointSite antarmuka, dan antarmuka induknya, IDTSSuspend. Antarmuka IDTSBreakpointSite menentukan interaksi antara mesin run-time dan tugas untuk membuat dan mengelola situs atau target titik henti kustom. Antarmuka IDTSSuspend menyediakan metode dan properti yang dipanggil oleh mesin run-time untuk memberi tahu tugas untuk menangguhkan atau melanjutkan eksekusinya.
Situs atau target titik henti adalah titik dalam eksekusi tugas di mana pemrosesan dapat ditangguhkan. Pengguna memilih dari situs titik henti yang tersedia dalam kotak dialog Atur Titik Henti . Misalnya, selain opsi titik henti default, Kontainer Perulangan Foreach menawarkan opsi "Istirahat di awal setiap perulangan perulangan".
Saat tugas mencapai target titik henti selama eksekusi, tugas mengevaluasi target titik henti untuk menentukan apakah titik henti diaktifkan. Ini menunjukkan bahwa pengguna ingin eksekusi berhenti pada titik henti tersebut. Jika titik henti diaktifkan, tugas akan menaikkan OnBreakpointHit peristiwa ke mesin run-time. Mesin run-time merespons peristiwa dengan memanggil metode Tangguhkan setiap tugas yang saat ini berjalan dalam paket. Eksekusi tugas dilanjutkan ketika runtime memanggil metode ResumeExecution dari tugas yang ditangguhkan.
Tugas yang tidak menggunakan titik henti masih harus mengimplementasikan IDTSBreakpointSite antarmuka dan IDTSSuspend . Ini memastikan bahwa tugas ditangguhkan dengan benar ketika objek lain dalam paket menaikkan OnBreakpointHit peristiwa.
IDTSBreakpointSite Interface dan BreakpointManager
Tugas membuat target titik henti dengan memanggil CreateBreakpointTarget metode BreakpointManager, menyediakan ID bilangan bulat dan deskripsi string sebagai parameter. Ketika tugas mencapai titik dalam kodenya yang berisi target titik henti, tugas mengevaluasi target titik henti dengan menggunakan IsBreakpointTargetEnabled metode untuk menentukan apakah titik henti tersebut diaktifkan. Jika true, tugas akan memberi tahu mesin run-time dengan menaikkan OnBreakpointHit peristiwa.
Antarmuka IDTSBreakpointSite mendefinisikan satu metode, AcceptBreakpointManager, yang dipanggil oleh mesin run-time selama pembuatan tugas. Metode ini menyediakan sebagai parameter BreakpointManager objek, yang kemudian digunakan oleh tugas untuk membuat dan mengelola titik hentinya. Tugas harus menyimpan BreakpointManager secara lokal untuk digunakan selama metode Validasi dan Jalankan.
Contoh kode berikut menunjukkan cara membuat target titik henti dengan menggunakan BreakpointManager. Sampel memanggil OnBreakpointHit metode untuk menaikkan peristiwa.
public void AcceptBreakpointManager( BreakpointManager breakPointManager )
{
// Store the breakpoint manager locally.
this.bpm = breakPointManager;
}
public override DTSExecResult Execute( Connections connections,
Variables variables, IDTSComponentEvents events,
IDTSLogging log, DtsTransaction txn)
{
// Create a breakpoint.
this.bpm.CreateBreakPointTarget( 1 , "A sample breakpoint target." );
...
if( this.bpm.IsBreakpointTargetEnabled( 1 ) == true )
events.OnBreakpointHit( this.bpm.GetBreakpointTarget( 1 ) );
}
Public Sub AcceptBreakpointManager(ByVal breakPointManager As BreakpointManager)
' Store the breakpoint manager locally.
Me.bpm = breakPointManager
End Sub
Public Overrides Function Execute(ByVal connections As Connections, _
ByVal variables As Variables, ByVal events As IDTSComponentEvents, _
ByVal log As IDTSLogging, ByVal txn As DtsTransaction) As DTSExecResult
' Create a breakpoint.
Me.bpm.CreateBreakPointTarget(1 , "A sample breakpoint target.")
If Me.bpm.IsBreakpointTargetEnabled(1) = True Then
events.OnBreakpointHit(Me.bpm.GetBreakpointTarget(1))
End If
End Function
Antarmuka IDTSSuspend
Antarmuka IDTSSuspend menentukan metode yang dipanggil oleh mesin run-time saat menjeda atau melanjutkan eksekusi tugas. Antarmuka IDTSSuspend diimplementasikan oleh IDTSBreakpointSite antarmuka, dan metode Suspend dan ResumeExecution biasanya ditimpa oleh tugas kustom. Ketika mesin run-time menerima peristiwa OnBreakpointHit dari tugas, mesin ini memanggil metode Tangguhkan setiap tugas yang sedang berjalan, memberi tahu tugas untuk menjeda. Ketika klien melanjutkan eksekusi, mesin run-time memanggil metode ResumeExecution tugas yang ditangguhkan.
Menangguhkan dan melanjutkan eksekusi tugas melibatkan jeda dan melanjutkan utas eksekusi tugas. Dalam kode terkelola, Anda melakukan ini menggunakan kelas ManualResetEvent di namespace System.Threading dari .NET Framework.
Sampel kode berikut menunjukkan penangguhan dan dimulainya kembali eksekusi tugas. Perhatikan bahwa metode Execute telah berubah dari sampel kode sebelumnya, dan utas eksekusi dijeda saat menembakkan titik henti.
private ManualResetEvent m_suspended = new ManualResetEvent( true );
private ManualResetEvent m_canExecute = new ManualResetEvent( true );
private int m_suspendRequired = 0;
private int m_debugMode = 0;
public override DTSExecResult Execute( Connections connections, Variables variables, IDTSComponentEvents events, IDTSLogging log, DtsTransaction txn)
{
// While a task is not executing, it is suspended.
// Now that we are executing,
// change to not suspended.
ChangeEvent(m_suspended, false);
// Check for a suspend before doing any work,
// in case the suspend and execute calls
// were initiated at virtually the same time.
CheckAndSuspend();
CheckAndFireBreakpoint( componentEvents, 1);
}
private void CheckAndSuspend()
{
// Loop until we can execute.
// The loop is required rather than a simple If
// because there is a time between the return from WaitOne and the
// reset that we might receive another Suspend call.
// Suspend() will see that we are suspended
// and return. So we need to rewait.
while (!m_canExecute.WaitOne(0, false))
{
ChangeEvent(m_suspended, true);
m_canExecute.WaitOne();
ChangeEvent(m_suspended, false);
}
}
private void CheckAndFireBreakpoint(IDTSComponentEvents events, int breakpointID)
{
// If the breakpoint is enabled, fire it.
if (m_debugMode != 0 && this.bpm.IsBreakpointTargetEnabled(breakpointID))
{
// Enter a suspend mode before firing the breakpoint.
// Firing the breakpoint will cause the runtime
// to call Suspend on this task.
// Because we are blocked on the breakpoint,
// we are suspended.
ChangeEvent(m_suspended, true);
events.OnBreakpointHit(this.bpm.GetBreakpointTarget(breakpointID));
ChangeEvent(m_suspended, false);
}
// Check for a suspension for two reasons:
// 1. If we are at a point where we could fire a breakpoint,
// we are at a valid suspend point. Even if we didn't hit a
// breakpoint, the runtime may have called suspend,
// so check for it.
// 2. Between the return from OnBreakpointHit
// and the reset of the event, it is possible to have
// received a suspend call from which we returned because
// we were already suspended. We need to be sure it is okay
// to continue executing now.
CheckAndSuspend();
}
static void ChangeEvent(ManualResetEvent e, bool shouldSet)
{
bool succeeded;
if (shouldSet)
succeeded = e.Set();
else
succeeded = e.Reset();
if (!succeeded)
throw new Exception("Synchronization object failed.");
}
public bool SuspendRequired
{
get {return m_suspendRequired != 0;}
set
{
// This lock is also taken by Suspend().
// Because it is possible for the package to be
// suspended and resumed in quick succession,
// this property might be set before
// the actual Suspend() call.
// Without the lock, the Suspend() might reset the canExecute
// event after we set it to abort the suspension.
lock (this)
{
Interlocked.Exchange(ref m_suspendRequired, value ? 1 : 0);
if (!value)
ResumeExecution();
}
}
}
public void ResumeExecution()
{
ChangeEvent( m_canExecute,true );
}
public void Suspend()
{
// This lock is also taken by the set SuspendRequired method.
// It prevents this call from overriding an
// aborted suspension. See comments in set SuspendRequired.
lock (this)
{
// If a Suspend is required, do it.
if (m_suspendRequired != 0)
ChangeEvent(m_canExecute, false);
}
// We can't return from Suspend until the task is "suspended".
// This can happen one of two ways:
// the m_suspended event occurs, indicating that the execute thread
// has suspended, or the canExecute flag is set,
// indicating that a suspend is no longer required.
WaitHandle [] suspendOperationComplete = {m_suspended, m_canExecute};
WaitHandle.WaitAny(suspendOperationComplete);
}
Private m_suspended As ManualResetEvent = New ManualResetEvent(True)
Private m_canExecute As ManualResetEvent = New ManualResetEvent(True)
Private m_suspendRequired As Integer = 0
Private m_debugMode As Integer = 0
Public Overrides Function Execute(ByVal connections As Connections, _
ByVal variables As Variables, ByVal events As IDTSComponentEvents, _
ByVal log As IDTSLogging, ByVal txn As DtsTransaction) As DTSExecResult
' While a task is not executing it is suspended.
' Now that we are executing,
' change to not suspended.
ChangeEvent(m_suspended, False)
' Check for a suspend before doing any work,
' in case the suspend and execute calls
' were initiated at virtually the same time.
CheckAndSuspend()
CheckAndFireBreakpoint(componentEvents, 1)
End Function
Private Sub CheckAndSuspend()
' Loop until we can execute.
' The loop is required rather than a simple if
' because there is a time between the return from WaitOne and the
' reset that we might receive another Suspend call.
' Suspend() will see that we are suspended
' and return. So we need to rewait.
Do While Not m_canExecute.WaitOne(0, False)
ChangeEvent(m_suspended, True)
m_canExecute.WaitOne()
ChangeEvent(m_suspended, False)
Loop
End Sub
Private Sub CheckAndFireBreakpoint(ByVal events As IDTSComponentEvents, _
ByVal breakpointID As Integer)
' If the breakpoint is enabled, fire it.
If m_debugMode <> 0 AndAlso Me.bpm.IsBreakpointTargetEnabled(breakpointID) Then
' Enter a suspend mode before firing the breakpoint.
' Firing the breakpoint will cause the runtime
' to call Suspend on this task.
' Because we are blocked on the breakpoint,
' we are suspended.
ChangeEvent(m_suspended, True)
events.OnBreakpointHit(Me.bpm.GetBreakpointTarget(breakpointID))
ChangeEvent(m_suspended, False)
End If
' Check for a suspension for two reasons:
' 1. If we are at a point where we could fire a breakpoint,
' we are at a valid suspend point. Even if we didn't hit a
' breakpoint, the runtime may have called suspend,
' so check for it.
' 2. Between the return from OnBreakpointHit
' and the reset of the event, it is possible to have
' received a suspend call from which we returned because
' we were already suspended. We need to be sure it is okay
' to continue executing now.
CheckAndSuspend()
End Sub
Shared Sub ChangeEvent(ByVal e As ManualResetEvent, ByVal shouldSet As Boolean)
Dim succeeded As Boolean
If shouldSet Then
succeeded = e.Set()
Else
succeeded = e.Reset()
End If
If (Not succeeded) Then
Throw New Exception("Synchronization object failed.")
End If
End Sub
Public Property SuspendRequired() As Boolean
Get
Return m_suspendRequired <> 0
End Get
Set
' This lock is also taken by Suspend().
' Because it is possible for the package to be
' suspended and resumed in quick succession,
' this property might be set before
' the actual Suspend() call.
' Without the lock, the Suspend() might reset the canExecute
' event after we set it to abort the suspension.
SyncLock Me
Interlocked.Exchange(m_suspendRequired,IIf(Value, 1, 0))
If (Not Value) Then
ResumeExecution()
End If
End SyncLock
End Set
End Property
Public Sub ResumeExecution()
ChangeEvent(m_canExecute,True)
End Sub
Public Sub Suspend()
' This lock is also taken by the set SuspendRequired method.
' It prevents this call from overriding an
' aborted suspension. See comments in set SuspendRequired.
SyncLock Me
' If a Suspend is required, do it.
If m_suspendRequired <> 0 Then
ChangeEvent(m_canExecute, False)
End If
End SyncLock
' We can't return from Suspend until the task is "suspended".
' This can happen one of two ways:
' the m_suspended event occurs, indicating that the execute thread
' has suspended, or the canExecute flag is set,
' indicating that a suspend is no longer required.
Dim suspendOperationComplete As WaitHandle() = {m_suspended, m_canExecute}
WaitHandle.WaitAny(suspendOperationComplete)
End Sub