Skip to content

feat(insight): add the remaining Workflow Insight exporters - #720

Open
wangyb-A wants to merge 1 commit into
mainfrom
feat/insight-exporters-parity
Open

feat(insight): add the remaining Workflow Insight exporters#720
wangyb-A wants to merge 1 commit into
mainfrom
feat/insight-exporters-parity

Conversation

@wangyb-A

@wangyb-A wangyb-A commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the eleven Workflow Insight exporters that were missing from the insight package: DynamoDB, Aurora, CloudWatch Logs, OTel, Firehose, EventBridge, Redshift, OpenSearch, SQS, HTTP, and File. Each is one module under exporters/, re-exported from the package root, with the same config fields, defaults, size limits, and failure behavior as the other SDKs' insight exporters. Adds OperationsFormat / apply_operations_format (array, by-name, both) used by the flexible-destination exporters.

boto3/botocore stay runtime-provided; no new dependency or extra. plugin.py, types.py, and the core SDK are untouched. S3Exporter gets a one-line type annotation so it, like the new exporters, is assignable to InsightExporter under mypy.

Small, deliberate differences from the other SDKs' exporters, each rejecting a configuration or input that could never succeed there either: SQS FIFO ids over 128 characters are hashed; Redshift requires exactly one of workgroup_name / cluster_identifier; OpenSearch basic auth requires both credentials at construction; the OTLP timeUnixNano keeps the record's microseconds; non-2xx HTTP bodies are read only up to 64 KiB for diagnostics; HTTP redirects are not followed (a 3xx is a failed export), so a POST is never re-sent as a body-less GET and configured credential headers never reach another origin.

Testing

  • One unit test file per exporter under packages/aws-durable-execution-sdk-python-insight/tests/, using injected recording clients that assert exact request kwargs and body bytes.
  • HTTP, OTel, and OpenSearch run against a local http.server; OpenSearch asserts the SigV4 Authorization header and the basic-auth header. File uses tmp_path in both modes.
  • tests/e2e/exporters_lifecycle_int_test.py drives the real plugin through the local durable runner with three exporters attached (FileExporter ndjson/by-name, FileExporter json, SQSExporter with a small size limit) in on-change mode, checking emission count, rendering, overwrite semantics, and per-exporter truncation.
  • hatch fmt --check, hatch run types:check, and hatch run test:all over the core, insight, otel, and testing packages pass.

Known follow-ups

  • OTelExporter and OpenSearchExporter send their request with no timeout, same as the reference exporters; a hung endpoint blocks the invocation-end hook until the function times out.

  • OTelExporter accepts any 2xx and does not inspect an OTLP partialSuccess body, same as the reference exporter.

  • RedshiftExporter submits the statement and does not await it, same as the reference exporter; documented in the README with a recommendation to use on-complete.

  • OTelExporter supports http/json only; http/protobuf raises at construction.

  • Repo-wide: every package's test package is named tests; under --import-mode=importlib the first import claims the name, so a conftest.py inside any non-core tests/ breaks the core SDK's tests.* imports.

@wangyb-A

Copy link
Copy Markdown
Contributor Author

/ai review

@wangyb-A
wangyb-A force-pushed the feat/insight-exporters-parity branch from ec3c719 to 32c68f1 Compare September 11, 2026 17:28
@wangyb-A
wangyb-A marked this pull request as ready for review September 11, 2026 18:01
@wangyb-A
wangyb-A deployed to ai-pr-review-runtime September 11, 2026 18:01 — with GitHub Actions Active
@wangyb-A
wangyb-A deployed to ai-pr-review-runtime September 11, 2026 18:25 — with GitHub Actions Active
Comment thread packages/aws-durable-execution-sdk-python-insight/README.md Outdated
@github-actions

This comment has been minimized.

@wangyb-A
wangyb-A force-pushed the feat/insight-exporters-parity branch from 32c68f1 to 7f24921 Compare September 11, 2026 21:30
@wangyb-A
wangyb-A deployed to ai-pr-review-runtime September 11, 2026 21:39 — with GitHub Actions Active
url: str,
headers: dict[str, str],
body: bytes,
timeout: float | None = None,

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeat of a finding from the previous run; disposition unchanged (see the reply on the earlier thread). Intentional: no timeout, same as the other SDKs' exporters; listed under Known follow-ups.

kwargs["DbUser"] = self.db_user
if self.secret_arn:
kwargs["SecretArn"] = self.secret_arn
self._client.execute_statement(**kwargs)

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeat of a finding from the previous run; disposition unchanged (see the reply on the earlier thread). Intentional: submit without awaiting, same as the other SDKs' Redshift exporter; README recommends on-complete.

Comment on lines +70 to +71
with path.open("a", encoding="utf-8") as handle:
handle.write(compact_dumps(formatted) + "\n")

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeat of a finding from the previous run; disposition unchanged (see the reply on the earlier thread). Per-execution files exist as mode="json"; README documents the shared-directory caveat.

return (
int(response.status),
str(response.reason or ""),
response.read().decode("utf-8", errors="replace"),

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 9b517c0: http_send no longer reads a success body and reads at most 64 KiB of a non-2xx body for the error message. Tests: test_http_exporter.py (200 KiB error body, 4 MiB success body) and test_opensearch_exporter.py (detail still truncated to 500 chars).

Comment on lines +44 to +48
if not workgroup_name and not cluster_identifier:
msg = (
"RedshiftExporter: provide either workgroup_name or cluster_identifier."
)
raise ValueError(msg)

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 9b517c0 for the target: exactly one of workgroup_name / cluster_identifier is now required at construction, with a negative test. Left the db_user/secret_arn combination unvalidated: I could not confirm which pairings the service rejects, and a wrong rule here would block valid configs.

Comment on lines +73 to +75
file_name = (
sanitize(record.get("executionName") or record["executionArn"])
+ ".json"

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeat of a finding from the previous run; disposition unchanged (see the reply on the earlier thread). Same file-name rule as S3Exporter on main and the other SDKs' file exporter.

Comment on lines +86 to +89
status, reason, _ = http_send("POST", self.endpoint, headers, body)
if not 200 <= status < 300:
msg = f"OTelExporter: OTLP endpoint returned {status} {reason}"
raise RuntimeError(msg)

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeat of a finding from the previous run; disposition unchanged (see the reply on the earlier thread). OTLP partialSuccess handling is listed under Known follow-ups.

Comment on lines +75 to +77
(`OperationsFormat`). `max_record_size_bytes` raises or lowers an exporter's
size limit; omitting it keeps the default. `HttpExporter` and `FileExporter`
have no default and do not truncate unless a limit is set.

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeat of a finding from the previous run; disposition unchanged (see the reply on the earlier thread). Omitting the argument keeps the default, matching the existing S3Exporter/LambdaLogExporter; README reworded in the previous push.

Comment on lines +77 to +79
(self.directory / file_name).write_text(
json.dumps(formatted, indent=2, ensure_ascii=False), encoding="utf-8"
)

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept as is: the limiter bounds the record body measured as compact JSON, not the destination's formatting, which is how the other SDKs' file exporter behaves too. FileExporter has no default limit, so this only affects callers who opt in.

Comment on lines +55 to +57
self.auth = OpenSearchAuth(auth)
self.username = username
self.password = password

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 9b517c0: auth="basic" raises ValueError at construction when username or password is None; an empty password is still accepted. Tests added for both.

Comment on lines +45 to +47
def _to_nano(iso: str) -> str:
millis = (parse_iso_datetime(iso) - _EPOCH) // datetime.timedelta(milliseconds=1)
return str(millis * 1_000_000)

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 9b517c0 with the suggested integer conversion: timeUnixNano now carries the record's microseconds. Test added for a fractional-millisecond emittedAt.

assert len(ndjson_files) == 1
lines = ndjson_files[0].read_text(encoding="utf-8").splitlines()
records = [json.loads(line) for line in lines]
assert len(records) >= 2, "on-change must emit RUNNING snapshots before the end"

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 9b517c0 after rebasing onto the asynchronous export change: the test no longer asserts a RUNNING-line count. It requires every line to be by-name shaped, non-terminal lines to be RUNNING, the last line to be the terminal SUCCEEDED record, and the SQS send count to match the file line count. Stable across repeated local runs.

Comment on lines +112 to +115
"scope": {
"name": _SCOPE_NAME,
"version": record.get("schemaVersion", ""),
},

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept as is: scope.version = schemaVersion is what the other SDKs' OTel exporter sends, and changing only this one would group Python telemetry differently. Worth changing in all SDKs together; noted as a follow-up.

@github-actions

This comment has been minimized.

@wangyb-A
wangyb-A force-pushed the feat/insight-exporters-parity branch from 7f24921 to 9b517c0 Compare September 11, 2026 22:34
Comment thread packages/aws-durable-execution-sdk-python-insight/README.md
@github-actions

This comment has been minimized.

@wangyb-A
wangyb-A force-pushed the feat/insight-exporters-parity branch from 9b517c0 to deda80a Compare September 11, 2026 23:04
@wangyb-A
wangyb-A deployed to ai-pr-review-runtime September 11, 2026 23:06 — with GitHub Actions Active
@github-actions

This comment has been minimized.

- DynamoDBExporter: PutItem, pk=executionArn, optional sk=emittedAt
- AuroraExporter: RDS Data API upsert, postgresql or mysql dialect
- CloudWatchLogsExporter: PutLogEvents into a per-day stream of any log group
- OTelExporter: OTLP/HTTP log record, http/json only
- FirehoseExporter: PutRecord, one JSON line per record
- EventBridgeExporter: PutEvents, DetailType = record status
- RedshiftExporter: Redshift Data API MERGE by execution_arn
- OpenSearchExporter: Index API PUT, SigV4 or basic auth
- SQSExporter: SendMessage, FIFO group and dedup ids
- HttpExporter: POST or PUT JSON with a timeout
- FileExporter: ndjson append or one json file per execution
- OperationsFormat / apply_operations_format shared by the flexible exporters
- README: exporter table and one setup block per exporter
@wangyb-A
wangyb-A force-pushed the feat/insight-exporters-parity branch from deda80a to 3a424e7 Compare September 11, 2026 23:26
@wangyb-A
wangyb-A deployed to ai-pr-review-runtime September 11, 2026 23:27 — with GitHub Actions Active
Comment on lines +54 to +55
self.db_user = db_user
self.secret_arn = secret_arn

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_lmu5i6xt6xpfayumozke7vmhli

[P2] Validate Redshift authentication combinations

db_user is valid only with a provisioned cluster using temporary credentials; it cannot accompany a workgroup or secret_arn. These combinations are accepted here and every export subsequently fails service validation, which the scheduler reduces to a warning. Reject invalid combinations during construction and test each supported authentication mode.

@github-actions

Copy link
Copy Markdown
Contributor

Codex AI review

Found eight actionable issues, including unbounded exporter waits, stale Redshift writes, and EFS corruption risks. Live AWS-service behavior remains the main residual test risk.

Reviewed commit 3a424e7bf6d3c768f38024007e42038eb43fcbde. Workflow run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant