versity / versitygw

versity s3 gateway
https://www.versity.com/products/versitygw/
Apache License 2.0
196 stars 25 forks source link

[Bug] - object versions concurrent upload #841

Open jonaustin09 opened 1 month ago

jonaustin09 commented 1 month ago

Describe the bug Versioning implementation doesn't correctly handle multiple concurrent object versions upload requests. Some of them cause internal server errors. e.g Internal Error, set content-type attr: xattr.Set test-bucket-1/my-obj user.content-type: no such file or directory

To Reproduce Run the following integration test against the gateway:

func Concurrent_upload_object_versions(s *S3Conf) error {
    testName := "Concurrent_upload_object_versions"
    return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
        obj := "my-obj"
        versionCount := 5
        // Channel to collect errors
        errCh := make(chan error, versionCount)

        uploadVersion := func(wg *sync.WaitGroup) {
            defer wg.Done()

            ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
            res, err := s3client.PutObject(ctx, &s3.PutObjectInput{
                Bucket: &bucket,
                Key:    &obj,
            })
            cancel()
            if err != nil {
                // Send error to the channel
                errCh <- err
                return
            }

            fmt.Printf("uploaded object successfully: versionId: %v\n", *res.VersionId)
        }

        wg := &sync.WaitGroup{}
        wg.Add(versionCount)

        for i := 0; i < versionCount; i++ {
            go uploadVersion(wg)
        }

        wg.Wait()
        close(errCh)

        // Check if there were any errors
        for err := range errCh {
            if err != nil {
                fmt.Printf("error uploading an object: %v\n", err.Error())
                return err
            }
        }

        // List object versions after all uploads
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
        res, err := s3client.ListObjectVersions(ctx, &s3.ListObjectVersionsInput{
            Bucket: &bucket,
        })
        cancel()
        if err != nil {
            return err
        }

        if len(res.Versions) != versionCount {
            return fmt.Errorf("expected %v object versions, instead got %v", versionCount, len(res.Versions))
        }

        return nil
    }, withVersioning())
}

Expected behavior The test should successfully pass without any errors.