public void insertOrUpsertStepInstance(StepInstance instance, boolean inserted) {
    final StepRuntimeState runtimeState = instance.getRuntimeState();
    final Map<StepDependencyType, StepDependencies> dependencies = instance.getDependencies();
    final Map<StepOutputsDefinition.StepOutputType, StepOutputs> outputs = instance.getOutputs();
    final Map<String, Artifact> artifacts = instance.getArtifacts();
    final Timeline timeline = instance.getTimeline();
    try {
      instance.setRuntimeState(null);
      instance.setDependencies(null);
      instance.setOutputs(null);
      instance.setArtifacts(null);
      instance.setTimeline(null);
      final String stepInstanceStr = toJson(instance);
      final String runtimeStateStr = toJson(runtimeState);
      final String stepDependenciesSummariesStr = toJson(dependencies);
      final String outputsStr = outputs == null ? null : toJson(outputs);
      final String artifactsStr = toJson(artifacts);
      final String[] timelineArray =
          timeline == null
              ? null
              : timeline.getTimelineEvents().stream().map(this::toJson).toArray(String[]::new);
      withMetricLogError(
          () ->
              withRetryableTransaction(
                  conn -> {
                    try (PreparedStatement stmt =
                        conn.prepareStatement(
                            inserted ? UPSERT_STEP_INSTANCE_QUERY : CREATE_STEP_INSTANCE_QUERY)) {
                      int idx = 0;
                      stmt.setString(++idx, stepInstanceStr);
                      stmt.setString(++idx, runtimeStateStr);
                      stmt.setString(++idx, stepDependenciesSummariesStr);
                      stmt.setString(++idx, outputsStr);
                      stmt.setString(++idx, artifactsStr);
                      stmt.setArray(++idx, conn.createArrayOf(ARRAY_TYPE_NAME, timelineArray));
                      return stmt.executeUpdate();
                    }
                  }),
          "insertOrUpsertStepInstance",
          "Failed to insert or upsert step instance {}[{}]",
          instance.getIdentity(),
          instance.getStepAttemptId());
    } finally {
      instance.setTimeline(timeline);
      instance.setArtifacts(artifacts);
      instance.setDependencies(dependencies);
      instance.setOutputs(outputs);
      instance.setRuntimeState(runtimeState);
    }
  }
@Test
  public void testInsertDuplicateStepInstance() {
    AssertHelper.assertThrows(
        "cannot insert the same step instance twice",
        ApplicationException.class,
        "BACKEND_ERROR - ERROR: duplicate key value",
        () -> stepDao.insertOrUpsertStepInstance(si, false));
  }