@@ -55,6 +55,15 @@ def _derive_rclone_config(self) -> str:
5555 'provider = AWS' ,
5656 'env_auth = true' ]
5757
58+ # Add region to config file
59+ # https://rclone.org/s3/#region
60+ if self .config .region :
61+ lines .append (f'region = { self .config .region } ' )
62+
63+ # Don't check if bucket exists or try to create it
64+ # https://rclone.org/s3/#no-check-bucket
65+ lines .append ('no_check_bucket = true' )
66+
5867 # Skip workloads that require checksums since rclone doesn't provide
5968 # config file options for checksum control
6069 if self .config .checksum :
@@ -104,7 +113,24 @@ def _derive_rclone_cmd(self) -> Tuple[list[str], Optional[bytes]]:
104113 if self .config .region :
105114 os .environ ['AWS_REGION' ] = self .config .region
106115
107- cmd .append ('copy' )
116+ # Determine the rclone command based on workload type
117+ # For RAM-based uploads (stdin), use rcat which is optimized for streaming
118+ # For RAM-based downloads, use cat which outputs to stdout (we'll redirect to /dev/null)
119+ # For everything else, use copy
120+ use_rcat = (num_tasks == 1 and
121+ first_task .action == 'upload' and
122+ not self .config .files_on_disk )
123+
124+ use_cat = (num_tasks == 1 and
125+ first_task .action == 'download' and
126+ not self .config .files_on_disk )
127+
128+ if use_rcat :
129+ cmd .append ('rcat' )
130+ elif use_cat :
131+ cmd .append ('cat' )
132+ else :
133+ cmd .append ('copy' )
108134
109135 # Add common configuration flags
110136 # For uploads: S3-specific multipart upload concurrency
@@ -115,6 +141,10 @@ def _derive_rclone_cmd(self) -> Tuple[list[str], Optional[bytes]]:
115141 # https://rclone.org/docs/#multi-thread-streams-int
116142 cmd += ['--multi-thread-streams' , str (concurrency )]
117143
144+ # Always transfer files, don't skip based on timestamps
145+ # https://rclone.org/docs/#i-ignore-times
146+ cmd += ['--ignore-times' ]
147+
118148 # Disable checksum when not specified
119149 # https://rclone.org/s3/#s3-disable-checksum
120150 if not self .config .checksum :
@@ -135,22 +165,24 @@ def _derive_rclone_cmd(self) -> Tuple[list[str], Optional[bytes]]:
135165 if first_task .action == 'download' :
136166 # src
137167 cmd .append (f'{ s3_remote } { self .config .bucket } /{ first_task .key } ' )
138- # dst
168+ # dst - only for copy command, not for cat
139169 if self .config .files_on_disk :
140170 cmd .append (first_task .key )
141- else :
142- cmd .append ('-' ) # output to stdout
171+ # For cat command, no dst needed - outputs to stdout which we redirect
143172
144173 else : # upload
145- # src
146174 if self .config .files_on_disk :
175+ # For copy command with files on disk
147176 cmd .append (first_task .key )
177+ # dst
178+ cmd .append (
179+ f'{ s3_remote } { self .config .bucket } /{ first_task .key } ' )
148180 else :
149- cmd . append ( '-' ) # read from stdin
181+ # For rcat command, stdin is implicit - only specify destination
150182 stdin = self ._random_data_for_upload [:first_task .size ]
151-
152- # dst
153- cmd . append ( f'{ s3_remote } { self .config .bucket } /{ first_task .key } ' )
183+ # dst only (rcat reads from stdin automatically)
184+ cmd . append (
185+ f'{ s3_remote } { self .config .bucket } /{ first_task .key } ' )
154186
155187 else :
156188 # Multiple files - need to use directory operations
@@ -257,20 +289,36 @@ def run(self):
257289 run_kwargs = {'args' : self ._rclone_cmd ,
258290 'input' : self ._stdin_for_rclone }
259291
292+ # For 'cat' command, redirect stdout to /dev/null
293+ devnull = None
294+ if 'cat' in self ._rclone_cmd :
295+ devnull = open ('/dev/null' , 'w' )
296+ run_kwargs ['stdout' ] = devnull
297+
260298 if self .config .verbose :
261299 # show live output, and immediately raise exception if process fails
262- print (f'> { subprocess .list2cmdline (self ._rclone_cmd )} ' , flush = True )
300+ print (f'> { subprocess .list2cmdline (self ._rclone_cmd )} > /dev/null' if devnull else f'> { subprocess . list2cmdline ( self . _rclone_cmd ) } ' , flush = True )
263301 run_kwargs ['check' ] = True
302+ # For verbose mode with cat, still capture stderr
303+ if devnull :
304+ run_kwargs ['stderr' ] = subprocess .PIPE
264305 else :
265306 # capture output, and only print if there's an error
266- run_kwargs ['capture_output' ] = True
267-
268- result = subprocess .run (** run_kwargs )
269- if result .returncode != 0 :
270- # show command that failed, and stderr if any
271- errmsg = f'{ subprocess .list2cmdline (self ._rclone_cmd )} '
272- if hasattr (result , 'stderr' ) and result .stderr :
273- stderr = result .stderr .decode ().strip ()
274- if stderr :
275- errmsg += f'\n { stderr } '
276- exit_with_error (errmsg )
307+ if not devnull :
308+ run_kwargs ['capture_output' ] = True
309+ else :
310+ run_kwargs ['stderr' ] = subprocess .PIPE
311+
312+ try :
313+ result = subprocess .run (** run_kwargs )
314+ if result .returncode != 0 :
315+ # show command that failed, and stderr if any
316+ errmsg = f'{ subprocess .list2cmdline (self ._rclone_cmd )} '
317+ if hasattr (result , 'stderr' ) and result .stderr :
318+ stderr = result .stderr .decode ().strip ()
319+ if stderr :
320+ errmsg += f'\n { stderr } '
321+ exit_with_error (errmsg )
322+ finally :
323+ if devnull :
324+ devnull .close ()
0 commit comments