All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- fix(load): submit managed loads as a job and poll, instead of holding one request open
-
Table storage layout, both directions.
add_managed_table()andcreate_managed_database()takepartition_by/sorted_by, andmanaged_table_layout()reads back what was actually declared as aTableLayout.TablePartitionKeyandTableSortKeyare re-exported so callers need one import.Both halves matter because a layout is fixed when the table is created and there is no alter path: a table declared without one keeps that shape until it is recreated and its data rewritten. So declaring is not enough — a caller has to be able to confirm it took, and to refuse to load when it cannot.
managed_table_layout()raisesKeyErrorfor a table that is not declared, rather than returning an empty layout. "Not there" and "declared without a layout" lead to opposite decisions for a caller.Until now this package could not express a layout at all, which is why at least one consumer hand-built the HTTP request instead. The generated key models are passed through rather than wrapped, so the transform vocabulary stays exactly the API's.
- Require
hotdata>=0.9.0,<0.10. 0.9.0 is the first release whose models carrypartition_by/sorted_byon the add-table request, the create-database table declarations, and the table-info response. On an olderhotdatathe fields would be silently dropped by the model and the table declared without a layout, returning success — which is the failure this feature exists to end.
- Cap the
hotdatadependency to the current minor (>=0.8.0,<0.9). This package wraps a generated client, so an SDK minor can remove a model field or aConfigurationkeyword this wrapper passes, and there is no regeneration step here to surface it — an uncapped floor turns an SDK release into a break in this package, in versions already published. Raise the cap deliberately after running the suite against the new minor.
-
Breaking: session/sandbox support is gone.
HotdataClientno longer acceptssession_id=,HotdataClient.session_idis removed,default_session_id()and theHOTDATA_SANDBOXread are gone, andlist_workspaces(),resolve_workspace_selection()andpick_workspace()lose theirsession_idparameter — note that loss is positional, so a three-argument call raises an arityTypeErrorrather than an unexpected-keyword one.workspace_health_lines()no longer emits asandboxline.Why now. The server stopped enforcing session scoping some time ago, so the value already reached nothing. What makes removal urgent rather than tidy is that the SDK is dropping the
SessionIdsecurity scheme: against that releaseConfiguration(session_id=...)raisesTypeErrorinstead of setting a header, and this package passed it unconditionally — so everyHotdataClient(...)would fail at construction. This package still pinshotdata<0.9, so nothing is broken today; the change is what lets the cap be raised later without a second breaking release.Migrating. Drop
session_id=fromHotdataClient(...), stop readingclient.session_id, stop settingHOTDATA_SANDBOX, and pass two arguments to the workspace helpers. Adapters that re-export session context in their own signatures — asession_id=parameter, asession_idmetadata key — need to remove it from theirs too, which makes their own release breaking in turn. -
hotdata_framework.httpanddefault_http_retries(). The module existed only to build theretries=policy removed under Fixed below, and had no other callers. It predateshotdata._retry, which supersedes it.
-
A
POSTis no longer replayed because of a response status.HotdataClientpassed its ownretries=intoConfiguration, which replaced the generated SDK's policy wholesale with one listingPOSTinallowed_methodsalongside a(502, 503, 504)forcelist — so an intermediary timing out a long request produced a silent, identical re-POSTwhile the server was still working on the first one. For a load that is not idempotent: the duplicate collides with the write lock the original holds and is refused.The override is removed and the SDK's own default now applies. It is the policy this wrapper was reaching for —
hotdata._retryretries a pre-response connection reset (the stale pooled socket case, where the server did no work) on any method, while leaving read timeouts and status retries idempotent-only.
create_index(database, table, columns=..., index_type=...)builds abm25,vector, orsortedindex on a managed table, matchinghotdata indexes createin the CLI. The build is a background job whose submit call reports success even when the build later fails, so this polls the job and raisesRuntimeErrorwith its error message;wait=Falsereturns as soon as the job is accepted. ReturnsCreateIndexResult, also exported.
list_managed_tables,load_managed_table,add_managed_table,delete_managed_table,delete_managed_database, andexecute_sqlaccept an already-resolvedManagedDatabase(as returned bycreate_managed_database) in place of a name/id. When passed one, they skip theget_database/list_databasesread probe. This lets an API key scoped to create + load but not read/databasesbootstrap a managed database and load into it within a single run: the caller holds theManagedDatabasefromcreateand drives the load/add/query ops with zero reads. The name/id string path is unchanged.
load_managed_tableaccepts akeyargument — the merge key columns fordelete/update/upsertloads, matched per-load instead of requiring a key declared at table creation. Omit it to use the table's declared key; ignored forreplace/append. Requireshotdata>=0.8.0.
upload_parquet()now delegates to the SDK'shotdata.uploads.UploadsApi.upload_file()instead of hand-rolling the session → PUT → finalize flow. Uploads gain concurrent part PUTs under a peak-memory budget, per-part retries, and ETag/size validation, making large uploads substantially faster. Errors still surface asRuntimeErrorwith the underlyingApiExceptionas the direct cause.
classify_sdk_errornow classifies HTTP 501 (Not Implemented) as terminal instead of transient — a permanent capability gap must not burn retries.
- The
POST /v1/filesfallback inupload_parquet(). Presigned upload sessions (POST /v1/uploads) are now required; a server that responds 501 raises a clearRuntimeErrorinstead of silently falling back to the full-file-in-memory upload path.
upload_parquet()now uses the presigned upload session API (POST /v1/uploads) instead of reading the entire file into memory before uploading. For multipart mode the file is streamed onepart_sizechunk at a time, eliminating the memory spike that caused OOM on large Parquet files. Falls back toPOST /v1/fileswhen the server returns 501.
load_managed_table(..., mode=...)selects the load mode (replace(default),append,delete,update,upsert) instead of always replacing the table.replace/appendapply the upload directly;delete/update/upsertmatch rows by the table's declared key. Backward compatible — omittingmodestill replaces.create_managed_database(..., keys={table: [cols]})andadd_managed_table(..., key=[cols])declare a table's row-identity key, enabling the key-based load modes on it. Requires ahotdataclient whose managed-table decl models carrykey(see the dependency floor bump); tables declared without a key stayreplace/append-only.
load_managed_table(..., mode="append")is no longer retried on transient errors. Every other mode is idempotent, but retrying anappendwhose commit succeeded before the response was received would duplicate the uploaded rows;appendnow runs at most once.modeis also now typed as a literal of the accepted values.
HotdataClientandManagedDatabaseClientacceptrequest_timeout(seconds, or a(connect, read)pair). The generated SDK otherwise issues every HTTP request with urllib3's no-timeout default, so a stalled or unreachable server blocks the calling thread indefinitely; the new parameter applies a socket-level deadline to every call through the client while still honoring an explicit per-call_request_timeout. Also exported asapply_default_request_timeout(api_client, timeout)for callers holding a raw generated client. Default remains no timeout (behavior unchanged unless opted in).
- Repository text cleanup: the changelog and test docstrings no longer reference external issue trackers. No functional changes; 0.6.2 is byte-identical to 0.6.1 in package code.
ManagedDatabaseClient.fetch_tablenow carries theX-Database-Idscope header on the result poll, the query-run poll, and the Arrow fetch — not only on the query submit. Results of database-scoped queries are themselves database-scoped, so every read against an existing synced table (merge/append loads, dlt state restore) failed with400: Bad Requestonce the table had data.- API error messages now include the response body (flattened, truncated to 500 chars).
400: Bad Requestalone hid the server's actual explanation.
- The
hotdataSDK dependency is now>=0.6.0, and the scope above rides its nativex_database_idparameters (get_result,get_query_run,get_result_arrow). Note 0.6.0 madex_database_idrequired onget_result_arrow, so older framework releases cannot run on it.
HotdataClient.add_managed_table(database, table, *, schema)declares a new table on an existing managed database (wrapping the SDKadd_database_tableendpoint). This allows additive schema evolution without recreating the database.
- Adopt the
hotdata0.5.0 SDK surface (dependency bumped from>=0.4.1to>=0.5.0). The release is backward compatible for everything the framework uses; the only API changes are additive (a new optionalformatfield onLoadManagedTableRequestand an optionalformatparameter onResultsApi.get_result), so no framework code changes were required.
ManagedDatabaseClient.fetch_tablenow waits for the persisted result to reachreadybefore fetching it as Arrow on the synchronous query path (it previously only waited on the async path). This fixes failures on read-modify-write loads (merge/append) and state reads against the live backend, where the result is often stillprocessingwhen the inline preview returns.
- Renamed the distribution from
hotdata-runtimetohotdata-frameworkand the import package fromhotdata_runtimetohotdata_framework. Consumers should depend onhotdata-frameworkand useimport hotdata_framework. The GitHub repository is nowsdk-python-framework. - Added PyPI classifiers, keywords, and an updated description identifying the project as a Python framework.
- Adopt the
hotdata0.4.1 SDK surface. - New typed error-handling public API:
HotdataError,HotdataTerminalError,HotdataTransientError, andclassify_sdk_error(hotdata_framework/errors.py). ManagedDatabaseClientfor managed database operations (hotdata_framework/managed_client.py).py.typedmarker so downstream consumers pick up inline type information.
- Bump the
hotdatadependency pin to>=0.4.1. - Add ruff and mypy tooling configuration and dev dependencies (
ruff>=0.5,mypy>=1.5); apply ruff lint/format cleanup across the package.
- Release 0.2.4
- Release 0.2.3
- Release 0.2.2
execute_sqlaccepts an optionaldatabasekeyword argument. When provided, the database name is resolved to an ID and sent as theX-Database-Idheader so SQL can reference managed database tables as"default"."<schema>"."<table>". Behaviour is unchanged whendatabaseis omitted.
- Switch managed database operations from the connections API to the dedicated
/databasesAPI (hotdata>=0.2.3required). create_managed_databasefirst parameter renamed fromnametodescription(keyword-only).ManagedDatabasedataclass: replacename/source_typefields withdescription/default_connection_id.resolve_managed_databasetries direct ID lookup first, then falls back to a description scan.list_managed_databasesnow fetches all databases regardless of source type.list_managed_tables,load_managed_table, anddelete_managed_tableusedefault_connection_idinstead of databaseidfor connection-scoped operations.
create_managed_databaseaccepts an optionalexpires_atparameter.
MANAGED_SOURCE_TYPE,build_managed_config, andcreate_connection_requestremoved from the public API.
- Managed database helpers on
HotdataClient.
- Initial release.