lazhen
2025-01-07 b3987122cbbc46c5c59d3173f37fca3170b6dc5a
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
Quintiq file version 2.0
#parent: #root
StaticMethod CopyDatasetRobust (LibOpt_Task task, String namenewdataset, String componentpositionname, 
  Key mdsid) as LibOpt_SnapshotReplannableCopyDataset
{
  Description:
  [*
    Copy the dataset in a slow (non-reactive) way that is robust against rollbacks and errors. 
    An optimizer run has to wait until the dataset copy is created before the optmizer run can continue. 
    Please override the `[YourOptimizationSubtype].GetMDSObject()` method before using this method.
  *]
  TextBody:
  [*
    // evr3 Mar-19-2020 (created)
    component := task.Component();
    run := component.Run();
    snapshot := LibOpt_SnapshotReplannableCopyDataset::Create( run,
                                                               task, 
                                                               namenewdataset, 
                                                               componentpositionname,                                              
                                                               true // IsMemoryOnly 
                                                               );
    // LibOpt_Optimization::GetMDSObject should be overridden.
    mdsobject := LibOpt_DatasetCopyConditional::GetMDSObject( run.Optimization() );
    snapshot.IsGetMDSObjectOverridden( not isnull( mdsobject ) );
    snapshot.HasFailedToCreateDataset( isnull( mdsobject ) );
    
    // Create the dataset after creating the snapshot, to ensure that the snapshot shows up in the 'snapshots' and 'replannable snapshots' forms. 
    if( not isnull( mdsobject ) )
    {
      parameters := MDSParameters::Create();
      // It is not possible to use StandAloneStorage. This results in empty datasets.
      // The dataset is later converted to a StandAloneStorage dataset in LibOpt_Component::DatasetCopyChangeToStandAlone()
      parameters.State( DatasetState::MemoryOnly().AsString() );  
    
      // An error can occur when mdsobject.Copy is called.
      // This happens, for example, when a circularity is created in the current transaction (but not propagated).
      // An error is also thrown if the name of the dataset copy is too long.
      // When the error occurs, the following steps happen:
      // - The onfailure block is entered
      // - HandleQuillErrorFromOnFailure is called
      // - If there is a dataset copy snapshot on the 'Handle error' component position, then the current method is called again.
      // - The try block is entered again.
      // - The dataset is copied succesfully (This dataset copy might be in a unpropagated or semi-propgated state)
      // - The HandleQuillErrorFromOnFailure call will roll back the current iteration to resolve the propagation error
      
      // Note: If another error happens when mdsobject.Copy is called the second time, 
      // then the if-block in the onfailure ensures that HandleQuillErrorFromOnFailure method won't be called again. This is to prevent an inescapable loop.
      try
      { 
        // Using mdsobject.Copy() is less efficient than using 'MDSObject::Create'. 
        // However, datasets created by using mdsobject.Copy() are not rolled back after an error or rollback. 
        mdsobject.Copy( namenewdataset, parameters );  
        snapshot.HasCreatedDataset( true );
      }
      onfailure
      {
        snapshot.HasFailedToCreateDataset( true );    
        numberofsnapshots := counter( task,
                                      SnapshotComponent.Children.astype( LibOpt_SnapshotReplannableCopyDataset ),
                                      snapshotreplannable,
                                      snapshotreplannable.ComponentPositionName() = componentpositionname );
        // In the unlikely case that a failure occurs every time that mdsobject.Copy() is called, then we could end up in an endless dataset creation loop.
        // Therefore, we attempt to create at most 1 additional dataset copy.
        if( numberofsnapshots <= 1 )
        {
          transaction := LibOpt_CurrentTransaction::GetCurrentTransaction( component );
          transaction.HandleQuillErrorFromOnFailure( e );
        }
      }
    }
    
    return snapshot;
  *]
  InterfaceProperties { Accessibility: 'Module' }
}