Skip to content

Apparent race condition deleting objects results in ENOENT #788

Open
@vlovich

Description

@vlovich

If I issue concurrent delete requests for objects that share a path, then the operation fails with a 500. Here's a sample typescript test using jest and the aws-sdk client library.

I think the root cause is that both are trying to do an rmdir up the chain of directories but they end up conflicting. Maybe rmdir should swallow ENOENT exceptions?

import { AWSError, S3 } from 'aws-sdk'
import S3rver from 's3rver'
import * as tmp from 'tmp'

function sync<T>(r: AWS.Request<T, AWS.AWSError>): Promise<T> {
  return new Promise((resolve, reject) => {
    r.send((err, data) => {
      if (err) {
        reject(err)
      } else {
        resolve(data)
      }
    })
  })
}

let s3: S3
let server: S3rver

beforeAll(() => {
    const serverDir = tmp.dirSync({ prefix: 's3-api.test', unsafeCleanup: true })

    server = new S3rver({
      port: 0,
      silent: false,
      directory: serverDir.name,
      configureBuckets: [
        {
          name: 'my-bucket',
          configs: [],
        },
      ],
    })
    const bound = await server.run()

    const s3 = new S3({
      region: 'us-east-1',
      accessKeyId: 'S3RVER',
      secretAccessKey: 'S3RVER',
      endpoint: `http://${bound.address}:${bound.port}`,
      s3ForcePathStyle: true,
    })
}

afterAll(async () => {
      await server.close()
})

test('s3rver racing delete', async ()  => {
    await Promise.all([
      sync(
        s3.putObject({
          Bucket: 'my-bucket',
          Key: '8ccf8a22d464800dccc55b90791fc6c43a867aeebda6d2772839f8a2105f9977/71f3db21645ab17a0f6fb561dad81608521e80a34d178646f738a98cb2ae8de4/0aa7509751f6c461ccd91c19e90360cccae3889ca83ecaa0415ff3bc0260f161/93bb309257da197aded081fa3de9c2c0',
        }),
      ),
      sync(
        s3.putObject({
          Bucket: 'my-bucket',
          Key: '8ccf8a22d464800dccc55b90791fc6c43a867aeebda6d2772839f8a2105f9977/71f3db21645ab17a0f6fb561dad81608521e80a34d178646f738a98cb2ae8de4/0aa7509751f6c461ccd91c19e90360cccae3889ca83ecaa0415ff3bc0260f161/93bb309257da197aded081fa3de9c2c0',
        }),
      ),
    ])

    await Promise.all([
      sync(
        s3.deleteObject({
          Bucket: 'my-bucket',
          Key: '8ccf8a22d464800dccc55b90791fc6c43a867aeebda6d2772839f8a2105f9977/71f3db21645ab17a0f6fb561dad81608521e80a34d178646f738a98cb2ae8de4/0aa7509751f6c461ccd91c19e90360cccae3889ca83ecaa0415ff3bc0260f161/93bb309257da197aded081fa3de9c2c0',
        }),
      ),
      sync(
        s3.deleteObject({
          Bucket: 'my-bucket',
          Key: '8ccf8a22d464800dccc55b90791fc6c43a867aeebda6d2772839f8a2105f9977/71f3db21645ab17a0f6fb561dad81608521e80a34d178646f738a98cb2ae8de4/0aa7509751f6c461ccd91c19e90360cccae3889ca83ecaa0415ff3bc0260f161/93bb309257da197aded081fa3de9c2c0',
        }),
      ),
    ])
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions