Transfers¶
push and pull move files between the local host and the provider, drawing a
live progress bar:
sx push ./video.mp4 /media/video.mp4 # host -> provider
sx pull /media/video.mp4 ./video.mp4 # provider -> host
Both stream, so a file larger than memory moves fine. Uploads detect a content type (from the extension, else by sniffing the head) and set it on backends that support it.
The bar is the ObservabilityLayer, which sx
attaches around upload and download for you. The same layer is available in
code when you want the events somewhere other than a terminal.
Both ends create their parents¶
push creates missing destination parents inside the storage root
(directories, or key prefixes on an object store), and pull creates missing
local ones. So this works with no prior mkdir:
What neither does is create the storage root itself. The configured S3/R2
bucket, Azure container, or ADLS filesystem must already exist, because
creating one is a provider control-plane operation rather than a filesystem
operation. That is what sx provision
is for, and where it cannot help it says so.
Tuning a transfer¶
sx reads the same STORIX_* environment your application does, so the
transfer knobs apply to the CLI without a separate config surface:
STORIX_MAX_TRANSFER_RANGES=1 sx pull /media/movie.mkv # one stream per file
STORIX_AZURE_READ_PREFETCH_SIZE=4194304 sx pull /media # less memory per stream
STORIX_S3_READ_CHUNK_SIZE=8388608 sx -p s3 pull /media # fewer, larger requests
What each one trades away - memory per in-flight transfer against requests per byte, and speed against transaction count - is in Tune transfers.
Stopping a transfer¶
Ctrl+C asks a running push or pull to stop, and it actually stops: every
stream unwinds at its next chunk boundary, the files that had not started never
start, and no worker keeps running behind your prompt. A half-written local
file is removed rather than left looking complete, so pull never leaves a
truncated file where a whole one belongs. The command reports it and exits
130, the shell's usual code for an interrupt:
A second Ctrl+C skips the graceful path and interrupts immediately.
What a stopped push leaves behind¶
One asymmetry to know: a stopped pull removes the destination it was writing,
a stopped push does not. What it leaves depends on the backend, because each
one commits a write differently (measured, mid-upload stop):
| backend | destination after a stopped push |
|---|---|
| Azure ADLS Gen2 | the path exists, length 0 - appended bytes are staged and never flushed |
| S3, GCS, Azure Blob | whatever the engine committed; an object store publishes on completion, so an interrupted upload may also leave an incomplete multipart upload behind |
| Local | the bytes written so far |
In every case the previous contents of that path are already gone: a push
truncates its destination when it opens it, so stopping does not preserve what
was there. storix does not then delete the path, for two reasons. The first is
that deleting cannot restore what the truncate destroyed, so it would trade a
wrong file for a missing one while the user is asking storix to stop doing
things. The second is that on an object store there may be nothing to delete
and the real leftover is an incomplete multipart upload, which the storage port
has no way to address.
Re-running the same push overwrites the destination. If you need a stop (or a
crash, or a dropped connection) to leave the destination untouched, that is
atomic writes - echo(atomic=True), write-temp-then-move - which is on the
roadmap and not implemented today.