Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# Bounded original-row collector source proposal

This is the next unexecuted #123 design after the supplied-row decoder in #283.
It specifies a fixture-only SQL collector, normalization and cancellation path;
it supplies no driver, launcher, installed dependency, database connection or
operating admission. `proposal.json` contains proposed transitions, bounds and
vectors, not observed results. `driver-sources.json` binds public source files
read as data, without importing them or retrieving a package archive.

The historical SDK, Server and Core identities, six cases and original failure
distinctions in `../proposal.json` remain authoritative. The updated observer and
decoder are source preparation, not an overlay on the historical installed SDK.
No #184 executor, acquisition source or blocked review is reused here.

## Proposed driver boundary

Use a dedicated observer process, with one connection and no retry, for each
before/after SQL pair. The proposed driver family is PyMySQL 1.2.3, source commit
`c990e86083c26493f3285b1e047902994c4b1811`. Its package archive, complete runtime
import closure, authentication/TLS dependencies and installed input are **not
selected or acquired**. Public release/source identity is only an input to this
proposal; it does not establish a supported or qualified historical tuple.

The process receives only the fixture identity and dedicated SELECT-only
fixture connection parameters through the future owner's private input channel.
Never obtain production endpoints, ambient credentials, config files, plugin
credentials or a user's home defaults. Network membership, peer identity,
authentication, grants and TLS trust must be fixed in the later input packet.
No opportunistic RSA package installation, plaintext fallback, server-key fetch,
reconnect, discovery or endpoint substitution is permitted.
For the proposed TCP path, supply an explicit SSLContext with the selected CA,
certificate verification and hostname verification. This pinned driver makes
explicit SSL configuration required, while its default preferred mode can fall
back to plaintext. Default TLS options are therefore inadmissible. The exact
context, peer and authentication closure still require selection and controls.

The driver source has consequential defaults:

- Connection setup sends `SET NAMES`; setting `autocommit=None`, `sql_mode=None`
and `init_command=None` avoids its optional further setup commands. The
collector must inventory the actual handshake and this observer-session
character-set statement. Require the driver's latest authenticated OK status
to show autocommit before either SELECT; refuse an inherited false state
without changing it. No transaction or time-zone change is proposed.
- Socket read/write timeouts bound socket operations, not the complete pair.
Buffered row reading and multipart packets have no proposed receive bound.
`max_allowed_packet` is not a receive-size limit.
- Default converters produce Python values including datetimes. The proposed
raw path disables Unicode and type-code decoders, retaining only the driver's
parameter encoders. A tuple cursor preserves positional fields; dictionary
mapping must follow exact, unique column-description validation.

These facts are bound to [connection source](https://github.com/PyMySQL/PyMySQL/blob/c990e86083c26493f3285b1e047902994c4b1811/pymysql/connections.py),
[cursor source](https://github.com/PyMySQL/PyMySQL/blob/c990e86083c26493f3285b1e047902994c4b1811/pymysql/cursors.py)
and [converter source](https://github.com/PyMySQL/PyMySQL/blob/c990e86083c26493f3285b1e047902994c4b1811/pymysql/converters.py).
The future adapter is still missing; stock driver use alone satisfies none of
the complete-read, byte-limit, absolute-deadline or cleanup obligations below.

## SQL and raw normalization

Keep the two accepted SELECT projections and original binary identity filters.
At adapter construction, translate only the closed named placeholder tokens
(`:namespace`, `:original_run_id`, `:original_task_id`,
`:workflow_instance_id`) to the driver's `%(name)s` syntax. Require the exact
expected token inventory in each immutable SQL text. Call `execute(query,
parameters)` with the validated identity dictionary; never format values into
SQL, quote them locally, log `mogrify`, or allow arbitrary SQL/token replacement.
The driver's parameter API is documented [here](https://pymysql.readthedocs.io/en/latest/modules/cursors.html#pymysql.cursors.Cursor.execute).
It performs driver escaping, not server-side prepared execution.

The proposed raw reader checks each requested byte count **before** calling the
pinned driver's `_read_bytes`. It refuses a request above 32 KiB or cumulative
receive reservation above 64 KiB for the entire connection/pair, including
handshake, setup, descriptions, rows and terminators. This also bounds multipart
assembly: header reservations and every component body consume the same budget.
A failed read does not refund that reservation or permit a retry. The adapter
must be source-reviewed against this exact private method before adoption; it
has not been implemented or exercised here.
Construct with `defer_connect=True`, initialize the deadline/receive guard before
any handshake, and connect only after fixture identity/input validation. The
complete process lifetime and its output are also bounded by the owner. Send
driver stdout/stderr to no retained log; only the selected framed channel may
publish fixed codes and allowlisted projection data.

Require the expected 23 task / 9 run descriptions, exact column order and unique
ASCII aliases before constructing a dictionary. Require all row tuples to have
the same exact length, at most two rows, and the complete expected result
terminator with no extra result set. Protocol errors, premature EOF, unexpected
LOCAL INFILE/auth exchange, extra results or changed metadata are refusals.
No `fetchmany(2)` or SQL `LIMIT 2` is a substitute for complete bounded reading.

Each scalar must be exactly bytes or null. Decode bytes as strict ASCII; refuse
other Python types or malformed bytes without calling `str` or `repr`. Preserve
null, empty bytes and literal `NULL` distinctly. Counters and presence bits stay
canonical decimal text for the existing decoder. Retain timestamp text without
timezone arithmetic; accept the declared 26-character microsecond form, or
right-pad a valid 19-character whole-second form with `.000000`. Refuse partial
fractions, date-only, zero/invalid dates and extra suffixes through the decoder.
Right-padding records zero fractional digits, not a recovered clock sample.

The capped raw capture is the selected projection's scalar bytes plus ordered
column metadata and terminal/length flags, not a packet dump. Authentication,
SQL parameter expansion, private lease-owner values, payloads and exception
text must never enter it. Proposed framed output is at most 32 KiB, with one
terminal document, exact byte length and no trailing output. Invalid output is
not parsed partially. Resource termination is a refusal, never success.

`capture_state` and `projection_state` remain separate. A complete zero-row
result has complete capture but a missing projection and no database clock.
Never promote a partial capture because the first row looks valid. Never borrow
the task clock for a missing run, or overwrite a before result with an after
result. Store the raw length/hash, normalized projection and fixed refusal code
under the exact case/phase/read identity; expose no arbitrary driver errors.

## One budget, monotonic brackets and teardown

At case entry set its unchanged 30-second monotonic deadline. For each snapshot
phase set `snapshot_deadline = min(case_deadline, phase_started + 5s,
total_deadline)`. The same deadline covers connection/authentication/setup,
both sequential SELECTs, normalization and output. Future registration/API
reads must share this phase deadline, not receive another five seconds each.
Record monotonic before/after brackets for the pair and each actual read, plus
the returned database UTC/timezone context when present.

An exhausted case budget makes a new before/after read unavailable. A started
read that overruns or is interrupted is truncated. A known protocol/shape
violation is invalid. None is complete. In particular, a failure at the case
deadline cannot acquire a fresh diagnostic budget. Required evidence can remain
missing: preserve the original failure and report an incomplete comparison.

The original outer cleanup allowance remains ten seconds, ending no later than
`min(case_deadline + 10s, total_deadline)`. It is a cleanup ceiling, not extra
snapshot time. Retain before observation on success/failure, then bracket worker
shutdown/drain, then attempt a separate same-original-row after observation
only when the original case budget remains. Every action receives the existing
absolute ceiling, without resetting it per connection/process/container.

The future fixture owner must run blocking driver work in an owned process;
thread cancellation or a timed-out coroutine does not prove a driver stopped.
Before its deadline, ordinary close may send COM_QUIT within the remaining
socket budget. At the deadline stop reading/draining, stop the exact child and
close/reap it within the shared cleanup ceiling. Do not call cursor cleanup
that consumes unread results after expiry. Independent owner-exit/deadline
cleanup, confirmed process exit, and final fixture-resource absence are required
before reporting cleanup complete. Any survivor retains an explicit incomplete
cleanup result and owner escalation; it cannot be relabeled complete.
Client exit/socket closure alone does not prove a server-side SELECT stopped.
On an interrupted case, the fixture owner's existing cleanup must stop and
verify absence of its owned backend within that same ceiling. Until then,
server-side query termination remains unknown; add no KILL/session mutation or
new query solely to manufacture a cancellation receipt.

This describes an unimplemented #123 fixture owner. It selects no supervisor,
platform-specific kill mechanism or reusable #184 custody implementation. Those
exact sources and control results belong in the later operating packet.

## Limits and next work

Each SQL result is a separate view. A common connection, UTC sample, timezone
label or nondecreasing monotonic bracket establishes no shared database/worker
clock, atomic snapshot, DATETIME storage domain or lease/deadline comparison.
Those comparisons remain unavailable until actual driver/session/storage/DST
behavior is bound and qualified.

Even complete task/run projections establish no current registration/private
claim/owner/attempt binding, full cloned-client/body coverage, hydration,
publication, cleanup causality or original root cause. Those independent parts
of the accepted comparator still require implementation and explicit
complete/missing/truncated/invalid states.

sdk-python next implements the proposed raw reader, closed SQL-token adapter,
normalizer and process/deadline integration with inert source controls from the
vector list. Server reviews historical fields, views and timestamp limits;
General owns later exact historical input/execution selection. Package/TLS/auth
closure, byte-capped actual transport, owner cleanup, image/platform digests and
historical installed-SDK provenance remain genuinely pending. No SQL/driver
import, package solve/install, container, historical API or runtime invocation
is authorized or performed by this source proposal.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"repository": "PyMySQL/PyMySQL",
"tag": "v1.2.3",
"commit": "c990e86083c26493f3285b1e047902994c4b1811",
"files": [
{
"path": "pymysql/connections.py",
"blob": "3de9fe0bcd62fff4132fa8a2e954a5e10361d45b",
"bytes": 57590,
"sha256": "cf3bf78cd872f87bbb95fab8e20579ccbb92b4a53d71ac34744bd16152f0dd64"
},
{
"path": "pymysql/cursors.py",
"blob": "5bc42bdfebcbd38742ce7623b401c3f79526fbc3",
"bytes": 16272,
"sha256": "53e045a28511dd8a5324cff98a8645f151fd76d20bbe2ebcefdc4b1ec32cbf75"
},
{
"path": "pymysql/converters.py",
"blob": "79000dba6ab297f9a2b7d6263c7e2e87c3400356",
"bytes": 10144,
"sha256": "f8adc54f4dabb1b8986d8697eaecca5df9ebc3bd90fcf6a1773a253c8356857d"
},
{
"path": "pymysql/protocol.py",
"blob": "52d149cba853c8d2449129c3bc64763380249f31",
"bytes": 11747,
"sha256": "a0b867ac55b3a82be794a15b63d900d834bfd883276a4ce1b4049a90bca5deb4"
},
{
"path": "pymysql/constants/FIELD_TYPE.py",
"blob": "b8b4486605fd1d712d3ad533df2c2ac137574b3d",
"bytes": 370,
"sha256": "cad1738009c699bf61bddb019672baf2a759bff6ba8d81485d3e954806fad33f"
},
{
"path": "pyproject.toml",
"blob": "e1afd4f8e5171aa139bc6ec3c41c9a38a7252ba8",
"bytes": 1492,
"sha256": "0c3fbc0b53bdaad91e4036c511662d282e7a858192a37e952c0a822a8bf8f7ca"
}
],
"status": "public-source-only-unexecuted-driver-proposal",
"package_archive": null,
"installed_import_closure": null,
"authentication_and_tls_closure": null,
"sdk_source_inputs": [
{
"repository": "durable-workflow/sdk-python",
"commit": "92aee3f49a9fffcdcacef1d7d924b68789e5c928",
"path": "tests/worker_startup_snapshot.py",
"blob": "fe28e5ff082f46908b0636caf0813e6bf1e2f419",
"bytes": 7588,
"sha256": "dd02a5dae1fe2a777a802efbc431844d3e9ca8c646303776db9f7e134640c0e8"
},
{
"repository": "durable-workflow/sdk-python",
"commit": "92aee3f49a9fffcdcacef1d7d924b68789e5c928",
"path": "tests/worker_observation.py",
"blob": "2edff9fc10d66681b9fea16cecafe2d9a8949b16",
"bytes": 16350,
"sha256": "ddc924ccd349cc6bba39d4ab389f98ee5c8fb355462b909081cbc08bb9b71e70"
},
{
"repository": "durable-workflow/sdk-python",
"commit": "92aee3f49a9fffcdcacef1d7d924b68789e5c928",
"path": "tests/integration/test_worker_task_families.py",
"blob": "bdee744d351c41ddb2270021cf324ba946e71824",
"bytes": 6863,
"sha256": "ef2ed0d2356e6feafe19b0e26111409939b5f9dc796ac6f7198831b6217a900b"
},
{
"repository": "durable-workflow/sdk-python",
"commit": "92aee3f49a9fffcdcacef1d7d924b68789e5c928",
"path": "tests/fixtures/worker_startup_reconstruction/proposal.json",
"blob": "a69037595508d840b135796bbddcdd67600df4ea",
"bytes": 5939,
"sha256": "ac8420da83604f7f890984e07ffb08af2ba4263496d01db01bfa153fa3ee62cf"
},
{
"repository": "durable-workflow/sdk-python",
"commit": "92aee3f49a9fffcdcacef1d7d924b68789e5c928",
"path": "tests/fixtures/worker_startup_reconstruction/sql-proposal/original-task.sql",
"blob": "5a0585997c61bd78a7bf3690aaad461e94870aef",
"bytes": 1140,
"sha256": "4385b4d0f08e22be9f053f282283111282f7215d12e9247d03ca0642e3e2b825"
},
{
"repository": "durable-workflow/sdk-python",
"commit": "92aee3f49a9fffcdcacef1d7d924b68789e5c928",
"path": "tests/fixtures/worker_startup_reconstruction/sql-proposal/original-run.sql",
"blob": "571721293b482fdcc4b0f8fbc5f43eaf63812198",
"bytes": 675,
"sha256": "025db37ab5311650d694f139941e206c2ae085d9b862426efa409a53ddd2ae13"
}
]
}
Loading
Loading