{
  "experimentId": "n8n-timeout-idempotency",
  "protocolVersion": "1.0.0",
  "n8nImage": "n8nio/n8n:2.30.5",
  "n8nImageDigest": "n8nio/n8n@sha256:450853cd21a2ce36587c4c860eb26927c1ceba9496bf55f4c213b5d3a6dc8c6f",
  "runtime": {
    "platform": "darwin",
    "architecture": "arm64",
    "dockerServerVersion": "28.3.3"
  },
  "startedAt": "2026-07-24T08:28:24.382Z",
  "completedAt": "2026-07-24T08:31:46.814Z",
  "workflowRuns": 40,
  "strategyObservations": 120,
  "repetitionsPerScenario": 10,
  "requestPolicy": {
    "maxTries": 3,
    "waitBetweenTriesMs": 100,
    "requestTimeoutMs": 300,
    "fixtureDelayMs": 800
  },
  "transientRecovery": {
    "retryWithoutKey": {
      "recovered": 20,
      "total": 20,
      "committedRecords": 50,
      "duplicateRecords": 30
    },
    "retryWithEnforcedKey": {
      "recovered": 20,
      "total": 20,
      "committedRecords": 20,
      "duplicateRecords": 0
    }
  },
  "persistentTimeout": {
    "retryWithoutKey": {
      "failed": 10,
      "committedRecords": 30,
      "duplicateRecords": 20
    },
    "retryWithEnforcedKey": {
      "failed": 10,
      "committedRecords": 10,
      "duplicateRecords": 0
    }
  },
  "ambiguousNoRetryFailures": {
    "failedExecutions": 30,
    "committedRecords": 30
  },
  "requests": 220,
  "committedRecords": 170,
  "protocolMismatches": 0,
  "scenarios": {
    "control_200": {
      "no_retry_no_key": {
        "observations": 10,
        "successes": 10,
        "failures": 0,
        "totalRequests": 10,
        "committedRecords": 10,
        "duplicateRecords": 0,
        "nodeDurationMs": {
          "min": 34,
          "p50": 39,
          "p95": 90,
          "max": 90
        }
      },
      "retry_no_key": {
        "observations": 10,
        "successes": 10,
        "failures": 0,
        "totalRequests": 10,
        "committedRecords": 10,
        "duplicateRecords": 0,
        "nodeDurationMs": {
          "min": 9,
          "p50": 10,
          "p95": 26,
          "max": 26
        }
      },
      "retry_with_key": {
        "observations": 10,
        "successes": 10,
        "failures": 0,
        "totalRequests": 10,
        "committedRecords": 10,
        "duplicateRecords": 0,
        "nodeDurationMs": {
          "min": 9,
          "p50": 10,
          "p95": 18,
          "max": 18
        }
      }
    },
    "timeout_once_after_commit": {
      "no_retry_no_key": {
        "observations": 10,
        "successes": 0,
        "failures": 10,
        "totalRequests": 10,
        "committedRecords": 10,
        "duplicateRecords": 0,
        "nodeDurationMs": {
          "min": 333,
          "p50": 349,
          "p95": 388,
          "max": 388
        }
      },
      "retry_no_key": {
        "observations": 10,
        "successes": 10,
        "failures": 0,
        "totalRequests": 20,
        "committedRecords": 20,
        "duplicateRecords": 10,
        "nodeDurationMs": {
          "min": 428,
          "p50": 432,
          "p95": 443,
          "max": 443
        }
      },
      "retry_with_key": {
        "observations": 10,
        "successes": 10,
        "failures": 0,
        "totalRequests": 20,
        "committedRecords": 10,
        "duplicateRecords": 0,
        "nodeDurationMs": {
          "min": 423,
          "p50": 430,
          "p95": 439,
          "max": 439
        }
      }
    },
    "timeout_twice_after_commit": {
      "no_retry_no_key": {
        "observations": 10,
        "successes": 0,
        "failures": 10,
        "totalRequests": 10,
        "committedRecords": 10,
        "duplicateRecords": 0,
        "nodeDurationMs": {
          "min": 340,
          "p50": 347,
          "p95": 355,
          "max": 355
        }
      },
      "retry_no_key": {
        "observations": 10,
        "successes": 10,
        "failures": 0,
        "totalRequests": 30,
        "committedRecords": 30,
        "duplicateRecords": 20,
        "nodeDurationMs": {
          "min": 849,
          "p50": 858,
          "p95": 864,
          "max": 864
        }
      },
      "retry_with_key": {
        "observations": 10,
        "successes": 10,
        "failures": 0,
        "totalRequests": 30,
        "committedRecords": 10,
        "duplicateRecords": 0,
        "nodeDurationMs": {
          "min": 836,
          "p50": 847,
          "p95": 867,
          "max": 867
        }
      }
    },
    "persistent_timeout_after_commit": {
      "no_retry_no_key": {
        "observations": 10,
        "successes": 0,
        "failures": 10,
        "totalRequests": 10,
        "committedRecords": 10,
        "duplicateRecords": 0,
        "nodeDurationMs": {
          "min": 339,
          "p50": 350,
          "p95": 381,
          "max": 381
        }
      },
      "retry_no_key": {
        "observations": 10,
        "successes": 0,
        "failures": 10,
        "totalRequests": 30,
        "committedRecords": 30,
        "duplicateRecords": 20,
        "nodeDurationMs": {
          "min": 1142,
          "p50": 1162,
          "p95": 1180,
          "max": 1180
        }
      },
      "retry_with_key": {
        "observations": 10,
        "successes": 0,
        "failures": 10,
        "totalRequests": 30,
        "committedRecords": 10,
        "duplicateRecords": 0,
        "nodeDurationMs": {
          "min": 1154,
          "p50": 1159,
          "p95": 1205,
          "max": 1205
        }
      }
    }
  },
  "conclusion": "In this commit-before-timeout protocol, retrying without an enforced idempotency key recovered transient executions but created duplicate records. The keyed branch recovered the same executions with one committed record per run. Persistent timeouts still failed, and every no-retry timeout had already committed a record despite the failed client result.",
  "limitations": [
    "The fixture is local and deterministic; it models a server that commits before delaying its response, not every timeout cause.",
    "The idempotency key is enforced by the fixture. A header alone provides no protection unless the receiving service implements a documented idempotency contract.",
    "The result covers n8n 2.30.5 HTTP Request nodes with a 300 ms timeout, fixed 100 ms wait and at most three total attempts.",
    "The experiment does not test payments, concurrent callers, key expiry, payload mismatch, queue mode, network partitions or provider-specific status lookup APIs.",
    "Ten repetitions per scenario validate the frozen deterministic protocol; they do not estimate production incident frequency."
  ]
}
