Skip to content

Commit 49d8bce

Browse files
ocampeauOlivier Campeau
authored andcommitted
Support for Conditionnal Write
This commit introduces support for conditional writes for major cloud providers: aws, gcp, azure. It does so my adding a new write options: IfNotExist. When set to true, conditions are set for each cloud storage driver to enable the desired behavior.
1 parent ab732c4 commit 49d8bce

File tree

5 files changed

+30
-0
lines changed

5 files changed

+30
-0
lines changed

blob/azureblob/azureblob.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,14 @@ func (b *bucket) NewTypedWriter(ctx context.Context, key, contentType string, op
913913
BlobContentType: &contentType,
914914
},
915915
}
916+
if opts.IfNotExist {
917+
etagAny := azcore.ETagAny
918+
uploadOpts.AccessConditions = &azblob.AccessConditions{
919+
ModifiedAccessConditions: &azblobblob.ModifiedAccessConditions{
920+
IfNoneMatch: &etagAny,
921+
},
922+
}
923+
}
916924
if opts.BeforeWrite != nil {
917925
asFunc := func(i any) bool {
918926
p, ok := i.(**azblob.UploadStreamOptions)

blob/blob.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,7 @@ func (b *Bucket) NewWriter(ctx context.Context, key string, opts *WriterOptions)
10981098
MaxConcurrency: opts.MaxConcurrency,
10991099
BeforeWrite: opts.BeforeWrite,
11001100
DisableContentTypeDetection: opts.DisableContentTypeDetection,
1101+
IfNotExist: opts.IfNotExist,
11011102
}
11021103
if len(opts.Metadata) > 0 {
11031104
// Services are inconsistent, but at least some treat keys
@@ -1422,6 +1423,13 @@ type WriterOptions struct {
14221423
// asFunc converts its argument to driver-specific types.
14231424
// See https://gocloud.dev/concepts/as/ for background information.
14241425
BeforeWrite func(asFunc func(any) bool) error
1426+
1427+
// IfNotExist is used for conditional writes. When set to true, if
1428+
// a blob exists for the same key in the bucket, the write operation
1429+
// won't take place.
1430+
// It only works for AWS S3, Google Storage and Azure. Setting this to
1431+
// true for any other driver is a no-op.
1432+
IfNotExist bool
14251433
}
14261434

14271435
// CopyOptions sets options for Copy.

blob/driver/driver.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ type WriterOptions struct {
110110
// asFunc allows drivers to expose driver-specific types;
111111
// see Bucket.As for more details.
112112
BeforeWrite func(asFunc func(any) bool) error
113+
114+
// IfNotExist is used for conditional writes. When set to true, if
115+
// a blob exists for the same key in the bucket, the write operation
116+
// won't take place.
117+
// It only works for AWS S3, Google Storage and Azure. Setting this to
118+
// true for any other driver is a no-op.
119+
IfNotExist bool
113120
}
114121

115122
// CopyOptions controls options for Copy.

blob/gcsblob/gcsblob.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,9 @@ func (b *bucket) NewTypedWriter(ctx context.Context, key, contentType string, op
626626
bkt := b.client.Bucket(b.name)
627627
obj := bkt.Object(key)
628628

629+
if opts.IfNotExist {
630+
obj = obj.If(storage.Conditions{DoesNotExist: true})
631+
}
629632
// Add an extra level of indirection so that BeforeWrite can replace obj
630633
// if needed. For example, ObjectHandle.If returns a new ObjectHandle.
631634
// Also, make the Writer lazily in case this replacement happens.

blob/s3blob/s3blob.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,10 @@ func (b *bucket) NewTypedWriter(ctx context.Context, key, contentType string, op
10621062
Key: aws.String(key),
10631063
Metadata: md,
10641064
}
1065+
if opts.IfNotExist {
1066+
// See https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-writes.html
1067+
reqV2.IfNoneMatch = aws.String("*")
1068+
}
10651069
if opts.CacheControl != "" {
10661070
reqV2.CacheControl = aws.String(opts.CacheControl)
10671071
}

0 commit comments

Comments
 (0)