/// <summary> /// Runs copies the datalink's current source/target tables and uses them as the basis for future test. /// </summary> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task <bool> RunSnapshot(CancellationToken cancellationToken) { try { var ct = CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token, cancellationToken); var token = ct.Token; token.ThrowIfCancellationRequested(); WriterResult.SetRunStatus(TransformWriterResult.ERunStatus.Started, null, null); var percent = 0; foreach (var step in _datalinkTest.DexihDatalinkTestSteps.OrderBy(c => c.Position).Where(c => c.IsValid)) { percent += 100 / _datalinkTest.DexihDatalinkTestSteps.Count; var datalink = _hub.DexihDatalinks.SingleOrDefault(c => c.IsValid && c.Key == step.DatalinkKey); if (datalink == null) { throw new DatalinkTestRunException( $"The datalink test {_datalinkTest.Name} failed as the datalink with the key {step.DatalinkKey} could not be found."); } // copy the expected results if (datalink.LoadStrategy == TransformWriterTarget.ETransformWriterMethod.Bulk) { foreach (var target in datalink.DexihDatalinkTargets) { var dbDatalinkTargetTable = _hub.GetTableFromKey(target.TableKey); if (dbDatalinkTargetTable == null) { throw new DatalinkTestRunException($"The table with key {target.TableKey} could not be found."); } var dbDatalinkTargetConnection = _hub.DexihConnections.SingleOrDefault(c => c.IsValid && c.Key == dbDatalinkTargetTable.ConnectionKey); if (dbDatalinkTargetConnection == null) { throw new DatalinkTestRunException($"The connection for the table {dbDatalinkTargetTable.Name} with the connection key {dbDatalinkTargetTable.Key} could not be found."); } var targetConnection = dbDatalinkTargetConnection.GetConnection(_transformSettings); var targetTable = dbDatalinkTargetTable.GetTable(_hub, targetConnection, _transformSettings); var dbExpectedConnection = _hub.DexihConnections.SingleOrDefault(c => c.IsValid && c.Key == step.ExpectedConnectionKey); if (dbExpectedConnection == null) { throw new DatalinkTestRunException($"The connection for the step {step.Name} with the connection key {step.ExpectedConnectionKey} could not be found."); } var dbExpectedTable = dbDatalinkTargetTable.CloneProperties(); dbExpectedTable.Name = step.ExpectedTableName; dbExpectedTable.Schema = step.ExpectedSchema; var expectedConnection = dbExpectedConnection.GetConnection(_transformSettings); var expectedTable = dbExpectedTable.GetTable(_hub, targetConnection, _transformSettings); UpdateProgress(percent); await expectedConnection.CreateTable(expectedTable, true, token); await using (var targetReader = targetConnection.GetTransformReader(targetTable)) await using (var targetReader2 = new ReaderConvertDataTypes(expectedConnection, targetReader)) { await targetReader2.Open(0, null, token); await expectedConnection.ExecuteInsertBulk(expectedTable, targetReader2, token); WriterResult.IncrementRowsCreated(targetReader2.TransformRows); WriterResult.IncrementRowsReadPrimary(targetReader2.TotalRowsReadPrimary); } } } else { // if there is no target table, then copy the outputs of the datalink. var dbExpectedConnection = _hub.DexihConnections.Single(c => c.IsValid && c.Key == step.ExpectedConnectionKey); var dbExpectedTable = datalink.GetOutputTable(); var expectedTable = dbExpectedTable.GetTable(null, null); expectedTable.Name = step.ExpectedTableName; expectedTable.Schema = step.ExpectedSchema; var expectedConnection = dbExpectedConnection.GetConnection(_transformSettings); var transformOperations = new TransformsManager(_transformSettings); var runPlan = transformOperations.CreateRunPlan(_hub, datalink, null, null, null, _transformWriterOptions); UpdateProgress(percent); await expectedConnection.CreateTable(expectedTable, true, cancellationToken); await using (var transform = runPlan.sourceTransform) await using (var transform2 = new ReaderConvertDataTypes(expectedConnection, transform)) { await transform2.Open(0, null, cancellationToken); await expectedConnection.ExecuteInsertBulk(expectedTable, transform2, cancellationToken); WriterResult.IncrementRowsCreated(transform2.TransformRows); WriterResult.IncrementRowsReadPrimary(transform2.TotalRowsReadPrimary); } } foreach (var testTable in step.DexihDatalinkTestTables) { var dbDatalinkTable = _hub.GetTableFromKey(testTable.TableKey); var dbDatalinkConnection = _hub.DexihConnections.Single(c => c.IsValid && c.Key == dbDatalinkTable.ConnectionKey); var datalinkConnection = dbDatalinkConnection.GetConnection(_transformSettings); var datalinkTable = dbDatalinkTable.GetTable(_hub, datalinkConnection, _transformSettings); var dbSourceConnection = _hub.DexihConnections.Single(c => c.IsValid && c.Key == testTable.SourceConnectionKey); var dbSourceTable = dbDatalinkTable.CloneProperties(); dbSourceTable.Name = testTable.SourceTableName; dbSourceTable.Schema = testTable.SourceSchema; var testConnection = dbSourceConnection.GetConnection(_transformSettings); var testTable1 = dbSourceTable.GetTable(_hub, testConnection, _transformSettings); UpdateProgress(percent); await testConnection.CreateTable(testTable1, true, cancellationToken); await using (var datalinkReader = datalinkConnection.GetTransformReader(datalinkTable)) await using (var datalinkReader2 = new ReaderConvertDataTypes(testConnection, datalinkReader)) { await datalinkReader2.Open(0, null, cancellationToken); await testConnection.ExecuteInsertBulk(testTable1, datalinkReader2, cancellationToken); WriterResult.IncrementRowsCreated(datalinkReader2.TransformRows); WriterResult.IncrementRowsReadPrimary(datalinkReader2.TotalRowsReadPrimary); } } } WriterResult.SetRunStatus(TransformWriterResult.ERunStatus.Finished, "Finished", null); await WriterResult.CompleteDatabaseWrites(); return(true); } catch (Exception ex) { WriterResult.SetRunStatus(TransformWriterResult.ERunStatus.Abended, ex.Message, ex); return(false); } }
public (Transform sourceTransform, Table sourceTable) CreateRunPlan(DexihHub hub, DexihDatalink hubDatalink, InputColumn[] inputColumns, long?maxDatalinkTransformKey, object maxIncrementalValue, TransformWriterOptions transformWriterOptions) //Last datatransform key is used to preview the output of a specific transform in the series. { try { _logger?.LogTrace($"CreateRunPlan {hubDatalink.Name} started."); var timer = Stopwatch.StartNew(); if (transformWriterOptions == null) { transformWriterOptions = new TransformWriterOptions(); } var primaryTransformResult = GetSourceTransform(hub, hubDatalink.SourceDatalinkTable, inputColumns, transformWriterOptions); var primaryTransform = primaryTransformResult.sourceTransform; var sourceTable = primaryTransformResult.sourceTable; var updateStrategy = hubDatalink.UpdateStrategy; //add a filter for the incremental column (if there is one) TableColumn incrementalCol = null; if (updateStrategy == EUpdateStrategy.AppendUpdateDeletePreserve || updateStrategy == EUpdateStrategy.AppendUpdatePreserve) { incrementalCol = primaryTransform.CacheTable?.GetColumn(EDeltaType.ValidFromDate); } else { incrementalCol = primaryTransform.CacheTable?.Columns.SingleOrDefault(c => c.IsIncrementalUpdate); } if (transformWriterOptions.ResetIncremental) { maxIncrementalValue = transformWriterOptions.ResetIncrementalValue; } if (maxDatalinkTransformKey == null && transformWriterOptions.IsEmptyTarget() == false && !(updateStrategy == EUpdateStrategy.Reload || updateStrategy == EUpdateStrategy.AppendUpdateDelete || updateStrategy == EUpdateStrategy.AppendUpdateDeletePreserve) && incrementalCol != null && maxIncrementalValue != null && maxIncrementalValue.ToString() != "") { var mappings = new Mappings() { new MapFilter(incrementalCol, maxIncrementalValue, ECompare.GreaterThan) }; var filterTransform = new TransformFilter(primaryTransform, mappings) { Name = $"Prefilter maxIncremental {maxIncrementalValue}" }; filterTransform.SetInTransform(primaryTransform); primaryTransform = filterTransform; } DexihTable targetTable = null; var target = hubDatalink.DexihDatalinkTargets.FirstOrDefault(c => c.NodeDatalinkColumnKey == null); if (target != null) { targetTable = hub.GetTableFromKey(target.TableKey); } _logger?.LogTrace($"CreateRunPlan {hubDatalink.Name}. Added incremental filter. Elapsed: {timer.Elapsed}"); //loop through the transforms to create the chain. foreach (var datalinkTransform in hubDatalink.DexihDatalinkTransforms.OrderBy(c => c.Position).Where(c => c.IsValid)) { //if this is an empty transform, then ignore it. if (datalinkTransform.DexihDatalinkTransformItems.Count == 0) { if (datalinkTransform.TransformType == ETransformType.Filter || datalinkTransform.TransformType == ETransformType.Mapping && datalinkTransform.PassThroughColumns) { if (datalinkTransform.Key == maxDatalinkTransformKey) { break; } continue; } } //if contains a join table, then add it in. Transform referenceTransform = null; if (datalinkTransform.JoinDatalinkTable != null) { var joinTransformResult = GetSourceTransform(hub, datalinkTransform.JoinDatalinkTable, null, transformWriterOptions); referenceTransform = joinTransformResult.sourceTransform; } var transform = datalinkTransform.GetTransform(hub, hubDatalink, transformWriterOptions.GlobalSettings, _transformSettings, primaryTransform, referenceTransform, targetTable, _logger); _logger?.LogTrace($"CreateRunPlan {hubDatalink.Name}, adding transform {datalinkTransform.Name}. Elapsed: {timer.Elapsed}"); primaryTransform = transform; if (datalinkTransform.Key == maxDatalinkTransformKey) { break; } } //if the maxDatalinkTransformKey is null (i.e. we are not doing a preview), and there are profiles add a profile transform. if (maxDatalinkTransformKey == null && hubDatalink.DexihDatalinkProfiles != null && hubDatalink.DexihDatalinkProfiles.Count > 0 && targetTable != null) { var profileRules = new Mappings(); foreach (var profile in hubDatalink.DexihDatalinkProfiles) { foreach (var column in targetTable.DexihTableColumns.Where(c => c.IsSourceColumn)) { var profileFunction = GetProfileFunction(profile.FunctionAssemblyName, profile.FunctionClassName, profile.FunctionMethodName, column.Name, profile.DetailedResults, transformWriterOptions.GlobalSettings); profileRules.Add(profileFunction); } } var transform = new TransformProfile(primaryTransform, profileRules) { Name = "User defined profiles" }; primaryTransform = transform; _logger?.LogTrace($"CreateRunPlan {hubDatalink.Name}, adding profiling. Elapsed: {timer.Elapsed}"); } if (transformWriterOptions.SelectQuery != null) { var transform = new TransformQuery(primaryTransform, transformWriterOptions.SelectQuery) { Name = "Select Query Filter" }; primaryTransform = transform; } _logger?.LogTrace($"CreateRunPlan {hubDatalink.Name}, completed. Elapsed: {timer.Elapsed}"); return(primaryTransform, sourceTable); } catch (Exception ex) { throw new TransformManagerException($"Create run plan failed. {ex.Message}", ex); } }
public async Task <bool> Run(CancellationToken cancellationToken) { try { cancellationToken.ThrowIfCancellationRequested(); WriterResult.StartTime = DateTime.Now; WriterResult.LastUpdateTime = DateTime.Now; var runStatus = WriterResult.SetRunStatus(ERunStatus.Started, null, null); if (!runStatus) { throw new DatajobRunException($"Failed to set status"); } if (_transformWriterOptions.TargetAction == TransformWriterOptions.ETargetAction.Truncate) { runStatus = WriterResult.SetRunStatus(ERunStatus.Started, "Truncating tables...", null); var targetTables = new HashSet <DexihTable>(); foreach (var step in Datajob.DexihDatalinkSteps.Where(c => c.IsValid)) { var datalink = _hub.DexihDatalinks.SingleOrDefault(c => c.IsValid && c.Key == step.DatalinkKey); if (datalink == null) { throw new DatajobRunException($"The datalink in the step {step.Name} with the datalink key {step.DatalinkKey} cound not be found."); } foreach (var target in datalink.DexihDatalinkTargets) { if (target.TableKey > 0) { var table = _hub.GetTableFromKey(target.TableKey); if (table != null) { targetTables.Add(table); } } } } // this loops through attempting to truncate tables one by one. // is repeats when there are failures to accomodate for some table having foriegn keys var atLeastOneSuccess = true; var atLeastOneFail = true; while (atLeastOneSuccess && atLeastOneFail) { atLeastOneSuccess = false; atLeastOneFail = false; var newTagetTables = new HashSet <DexihTable>(); foreach (var dbTable in targetTables) { var dbConnection = _hub.DexihConnections.SingleOrDefault(c => c.Key == dbTable.ConnectionKey); if (dbConnection == null) { throw new DatajobRunException($"The connection for the table {dbTable.Name} with the connection key {dbTable.ConnectionKey} could not be found."); } var connection = dbConnection.GetConnection(_transformSettings); var table = dbTable.GetTable(_hub, connection, _transformSettings); try { await connection.TruncateTable(table, cancellationToken); atLeastOneSuccess = true; } catch { atLeastOneFail = true; newTagetTables.Add(dbTable); } } targetTables = newTagetTables; } if (targetTables.Count > 0) { var message = $"The job failed as the following tables could not be truncated: {string.Join(", ", targetTables.Select(c => c.Name))}"; WriterResult.SetRunStatus(ERunStatus.Abended, message, null); return(false); } } var inputParameters = new InputParameters(); foreach (var parameter in Datajob.Parameters) { inputParameters.Add(new InputParameter() { Name = parameter.Name, Value = parameter.Value, Rank = parameter.Rank }); } //start all jobs async foreach (var step in Datajob.DexihDatalinkSteps.Where(c => c.IsValid)) { var datalink = _hub.DexihDatalinks.SingleOrDefault(c => c.IsValid && c.Key == step.DatalinkKey); if (datalink == null) { throw new DatajobRunException($"The step {step.Name} contains a datalink with the key {step.DatalinkKey} which can not be found."); } foreach (var parameter in step.Parameters) { parameter.Value = inputParameters.SetParameters(parameter.Value, parameter.Rank); } var transformSettings = new TransformSettings { HubVariables = _transformSettings.HubVariables, RemoteSettings = _transformSettings.RemoteSettings, InputParameters = step.Parameters.ToArray <InputParameterBase>(), ClientFactory = _transformSettings.ClientFactory }; var inputColumns = step.DexihDatalinkStepColumns.Select(c => c.ToInputColumn()).ToArray(); var datalinkRun = new DatalinkRun(transformSettings, _logger, WriterResult.AuditKey, datalink, _hub, inputColumns, _transformWriterOptions, _alertQueue, _alertEmails) { DatalinkStepKey = step.Key }; DatalinkSteps.Add(datalinkRun); //start datalinks that have no dependencies. if (step.DexihDatalinkDependencies == null || step.DexihDatalinkDependencies.Count == 0) { StartDatalink(datalinkRun); } } WriterResult.SetRunStatus(ERunStatus.Running, null, null); await WaitUntilFinished(); return(true); } catch (OperationCanceledException) { Cancel(); await WaitUntilFinished(); WriterResult.SetRunStatus(ERunStatus.Cancelled, "Datajob was cancelled", null); throw new DatajobRunException($"The datajob {Datajob.Name} was cancelled."); } catch (Exception ex) { Cancel(); await WaitUntilFinished(); var message = $"The job {Datajob.Name} failed. {ex.Message}"; WriterResult?.SetRunStatus(ERunStatus.Abended, message, ex); throw new DatajobRunException(message, ex); } finally { await WriterResult.CompleteDatabaseWrites(); OnFinish?.Invoke(this); } }
public (Transform sourceTransform, Table sourceTable) GetSourceTransform(DexihHub hub, DexihDatalinkTable hubDatalinkTable, InputColumn[] inputColumns, TransformWriterOptions transformWriterOptions) { try { Transform sourceTransform; Table sourceTable; var referenceTableAlias = hubDatalinkTable.Key.ToString(); switch (hubDatalinkTable.SourceType) { case ESourceType.Datalink: var datalink = hub.DexihDatalinks.SingleOrDefault(c => c.Key == hubDatalinkTable.SourceDatalinkKey); if (datalink == null) { throw new TransformManagerException($"The source datalink with the key {hubDatalinkTable.SourceDatalinkKey} was not found"); } (sourceTransform, sourceTable) = CreateRunPlan(hub, datalink, inputColumns, null, false, null); break; case ESourceType.Table: if (hubDatalinkTable.SourceTableKey == null) { throw new TransformManagerException($"The source table key was null."); } var sourceDbTable = hub.GetTableFromKey(hubDatalinkTable.SourceTableKey.Value); if (sourceDbTable == null) { throw new TransformManagerException($"The source table with the key {hubDatalinkTable.SourceTableKey.Value} could not be found."); } var sourceDbConnection = hub.DexihConnections.SingleOrDefault(c => c.Key == sourceDbTable.ConnectionKey && c.IsValid); if (sourceDbConnection == null) { throw new TransformException($"The connection with key {sourceDbTable.ConnectionKey} could not be found."); } var sourceConnection = sourceDbConnection.GetConnection(_transformSettings); sourceTable = sourceDbTable.GetTable(hub, sourceConnection, inputColumns, _transformSettings, referenceTableAlias); if (hubDatalinkTable.DisableVersioning) { sourceTable.IsVersioned = false; } sourceTransform = sourceConnection.GetTransformReader(sourceTable, transformWriterOptions.PreviewMode); break; case ESourceType.Rows: var rowCreator = new ReaderRowCreator(); rowCreator.InitializeRowCreator(hubDatalinkTable.RowsStartAt ?? 1, hubDatalinkTable.RowsEndAt ?? 1, hubDatalinkTable.RowsIncrement ?? 1); rowCreator.TableAlias = hubDatalinkTable.Key.ToString(); sourceTable = rowCreator.GetTable(); sourceTransform = rowCreator; break; case ESourceType.Function: sourceTable = hubDatalinkTable.GetTable(null, inputColumns); var data = new object[sourceTable.Columns.Count]; for (var i = 0; i < sourceTable.Columns.Count; i++) { data[i] = sourceTable.Columns[i].DefaultValue; } sourceTable.Data.Add(data); var defaultRow = new ReaderDynamic(sourceTable); defaultRow.Reset(); sourceTransform = defaultRow; break; default: throw new TransformManagerException($"Error getting the source transform."); } sourceTransform.TableAlias = referenceTableAlias; // compare the table in the transform to the source datalink columns. If any are missing, add a mapping // transform to include them. var transformColumns = sourceTransform.CacheTable.Columns; var datalinkColumns = hubDatalinkTable.DexihDatalinkColumns; // add a mapping transform to include inputColumns. var mappings = new Mappings(); foreach (var column in datalinkColumns.Where(c => c.DeltaType != EDeltaType.IgnoreField)) { var transformColumn = transformColumns.SingleOrDefault(c => c.Name == column.Name); if (transformColumn == null) { var newColumn = column.GetTableColumn(inputColumns); mappings.Add(new MapInputColumn(newColumn)); } else { transformColumn.DeltaType = column.DeltaType; transformColumn.IsIncrementalUpdate = column.IsIncrementalUpdate; } } if (mappings.Count > 0) { sourceTransform = new TransformMapping(sourceTransform, mappings) { Name = "Input Column Mapping" }; } sourceTransform.IgnoreQuery = hubDatalinkTable.DisablePushDown; return(sourceTransform, sourceTable); } catch (Exception ex) { throw new TransformManagerException($"Get source transform failed. {ex.Message}", ex); } }