@@ -27,27 +27,12 @@ import {
2727import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
2828import { ERRORS , StorageBackendError } from '@internal/errors'
2929import { getConfig } from '../../config'
30- import { addAbortSignal , PassThrough , Readable } from 'node:stream'
31- import { trace } from '@opentelemetry/api'
32- import { createByteCounterStream } from '@internal/concurrency'
33- import { AgentStats , createAgent , gatherHttpAgentStats , InstrumentedAgent } from '@internal/http'
30+ import { Readable } from 'node:stream'
31+ import { createAgent , InstrumentedAgent } from '@internal/http'
32+ import { monitorStream } from '@internal/streams'
3433
3534const { tracingFeatures, storageS3MaxSockets, tracingEnabled } = getConfig ( )
3635
37- interface StreamStatus {
38- time : Date
39- bytesUploaded : number
40- progress : Progress [ ]
41- dataStream : {
42- closed : boolean
43- paused : boolean
44- errored : boolean
45- writable : boolean
46- byteRead : number
47- }
48- httpAgentStats : AgentStats
49- }
50-
5136export interface S3ClientOptions {
5237 endpoint ?: string
5338 region ?: string
@@ -154,22 +139,19 @@ export class S3Backend implements StorageBackendAdapter {
154139 throw ERRORS . Aborted ( 'Upload was aborted' )
155140 }
156141
157- const streamWatcher = tracingFeatures ?. upload ? this . watchUploadStream ( body , signal ) : undefined
158- const uploadStream = streamWatcher ? streamWatcher . dataStream : body
142+ const dataStream = tracingFeatures ?. upload ? monitorStream ( body ) : body
159143
160144 const upload = new Upload ( {
161145 client : this . client ,
162146 params : {
163147 Bucket : bucketName ,
164148 Key : withOptionalVersion ( key , version ) ,
165- Body : uploadStream ,
149+ Body : dataStream ,
166150 ContentType : contentType ,
167151 CacheControl : cacheControl ,
168152 } ,
169153 } )
170154
171- streamWatcher ?. watchUpload ( upload )
172-
173155 signal ?. addEventListener (
174156 'abort' ,
175157 ( ) => {
@@ -178,6 +160,12 @@ export class S3Backend implements StorageBackendAdapter {
178160 { once : true }
179161 )
180162
163+ if ( tracingFeatures ?. upload ) {
164+ upload . on ( 'httpUploadProgress' , ( progress : Progress ) => {
165+ dataStream . emit ( 's3_progress' , JSON . stringify ( progress ) )
166+ } )
167+ }
168+
181169 try {
182170 const data = await upload . done ( )
183171 const metadata = await this . headObject ( bucketName , key , version )
@@ -194,26 +182,9 @@ export class S3Backend implements StorageBackendAdapter {
194182 }
195183 } catch ( err ) {
196184 if ( err instanceof Error && err . name === 'AbortError' ) {
197- const span = trace . getActiveSpan ( )
198- if ( span ) {
199- // Print how far we got uploading the file
200- const lastSeenStatus = streamWatcher ?. lastSeenStreamStatus
201- const lastStreamStatus = streamWatcher ?. getStreamStatus ( )
202-
203- if ( lastSeenStatus && lastStreamStatus ) {
204- const { progress, ...lastSeenStream } = lastSeenStatus
205- span . setAttributes ( {
206- lastStreamStatus : JSON . stringify ( lastStreamStatus ) ,
207- lastSeenStatus : JSON . stringify ( lastSeenStream ) ,
208- } )
209- }
210- }
211-
212185 throw ERRORS . AbortedTerminate ( 'Upload was aborted' , err )
213186 }
214187 throw StorageBackendError . fromError ( err )
215- } finally {
216- streamWatcher ?. stop ( )
217188 }
218189 }
219190
@@ -493,92 +464,6 @@ export class S3Backend implements StorageBackendAdapter {
493464 this . agent . close ( )
494465 }
495466
496- protected watchUploadStream ( body : Readable , signal ?: AbortSignal ) {
497- const passThrough = new PassThrough ( )
498-
499- if ( signal ) {
500- addAbortSignal ( signal , passThrough )
501- }
502-
503- passThrough . on ( 'error' , ( ) => {
504- body . unpipe ( passThrough )
505- } )
506-
507- body . on ( 'error' , ( err ) => {
508- if ( ! passThrough . closed ) {
509- passThrough . destroy ( err )
510- }
511- } )
512-
513- const byteReader = createByteCounterStream ( )
514- const bodyStream = body . pipe ( passThrough )
515-
516- // Upload stats
517- const uploadProgress : Progress [ ] = [ ]
518- const getStreamStatus = ( ) : StreamStatus => ( {
519- time : new Date ( ) ,
520- bytesUploaded : uploadProgress [ uploadProgress . length - 1 ] ?. loaded || 0 ,
521- dataStream : {
522- closed : bodyStream . closed ,
523- paused : bodyStream . isPaused ( ) ,
524- errored : Boolean ( bodyStream . errored ) ,
525- writable : bodyStream . writable ,
526- byteRead : byteReader . bytes ,
527- } ,
528- httpAgentStats : gatherHttpAgentStats ( this . agent . httpsAgent . getCurrentStatus ( ) ) ,
529- progress : uploadProgress ,
530- } )
531-
532- let streamStatus = getStreamStatus ( )
533-
534- const streamWatcher = setInterval ( ( ) => {
535- streamStatus = getStreamStatus ( )
536- } , 1000 )
537-
538- const dataStream = passThrough . pipe ( byteReader . transformStream )
539-
540- body . on ( 'error' , ( err ) => {
541- passThrough . destroy ( err )
542- } )
543-
544- passThrough . on ( 'error' , ( err ) => {
545- body . destroy ( err )
546- } )
547-
548- passThrough . on ( 'close' , ( ) => {
549- body . unpipe ( passThrough )
550- } )
551-
552- function watchUpload ( upload : Upload ) {
553- upload . on ( 'httpUploadProgress' , ( progress ) => {
554- uploadProgress . push ( {
555- total : progress . total ,
556- part : progress . part ,
557- loaded : progress . loaded ,
558- } )
559- if ( uploadProgress . length > 100 ) {
560- uploadProgress . shift ( )
561- }
562- } )
563- }
564-
565- return {
566- dataStream,
567- byteReader,
568- get uploadProgress ( ) {
569- return uploadProgress
570- } ,
571- get lastSeenStreamStatus ( ) {
572- return streamStatus
573- } ,
574- getStreamStatus,
575- stop ( ) {
576- clearInterval ( streamWatcher )
577- } ,
578- watchUpload,
579- }
580- }
581-
582467 protected createS3Client ( options : S3ClientOptions & { name : string } ) {
583468 const params : S3ClientConfig = {
584469 region : options . region ,
0 commit comments