【问题标题】:Does AWS S3 GetObject read the partial of the Object being uploaded to s3 at the same timeAWS S3 GetObject 是否同时读取正在上传到 s3 的部分对象
【发布时间】:2021-07-15 01:33:10
【问题描述】:

我有一个 lambda (L1) 将文件 (100MB) 替换到 s3 位置 (s3://bucket/folder/abc.json)。我有另一个 lambda(L2,L3)同时读取同一个文件,一个通过 golang api 另一个通过 Athena 查询。 s3 bucket/ 文件夹没有版本控制。

问题是:在新文件上传之前,lambdas L2、L3 是否会读取文件的旧副本?或者它是否读取正在上传的部分文件?如果是后者,那么如何确保 L2、L3 仅在完整上传时读取文件?

【问题讨论】:

    标签: amazon-web-services amazon-s3


    【解决方案1】:

    Amazon S3 现在是 strongly consistent。这意味着一旦您上传了一个对象,所有阅读该对象的人都可以保证获得该对象的更新版本。

    从表面上看,这听起来可以保证您的问题是“是的,所有客户端都会获得文件的旧版本或新版本”。真相比这还要模糊。

    在幕后,许多 S3 API 通过分段上传进行上传。这是众所周知的,并且不会改变我上面所说的,因为上传必须在对象可用之前完成。但是,许多 API 在下载期间也使用多个字节范围请求来下载更大的对象。这是有问题的。这意味着下载可能会下载文件v1的一部分,然后当它去下载另一部分时,如果v2刚刚上传,它可能会得到v2。

    稍加努力,我们就可以证明这一点:

    #!/usr/bin/env python3
    
    import boto3
    import multiprocessing
    import io
    import threading
    
    bucket = "a-bucket-to-use"
    key = "temp/dummy_key"
    size = 104857600
    
    class ProgressWatcher:
        def __init__(self, filesize, downloader):
            self._size = float(filesize)
            self._seen_so_far = 0
            self._lock = threading.Lock()
            self._launch = True
            self.downloader = downloader
    
        def __call__(self, bytes_amount):
            with self._lock:
                self._seen_so_far += bytes_amount
                if self._launch and (self._seen_so_far / self._size) >= 0.95:
                    self._launch = False
                    self.downloader.start()
    
    def upload_helper(pattern, name, callback):
        # Upload a file of 100mb of "pattern" bytes
        s3 = boto3.client('s3')
        print(f"Uploading all {name}..")
        temp = io.BytesIO(pattern * size)
        s3.upload_fileobj(temp, bucket, key, Callback=callback)
        print(f"Done uploading all {name}")
    
    def download_helper():
        # Download a file
        s3 = boto3.client('s3')
        print("Starting download...")
        s3.download_file(bucket, key, "temp_local_copy")
        print("Done with download")
    
    def main():
        # See how long an upload takes
        upload_helper(b'0', "zeroes", None)
    
        # Watch how the next upload progresses, this will start a download when it's nearly done
        watcher = ProgressWatcher(size, multiprocessing.Process(target=download_helper))
        # Start another upload, overwriting the all-zero file with all-ones
        upload_helper(b'1', "ones", watcher)
    
        # Wait for the downloader to finish
        watcher.downloader.join()
    
        # See what the resulting file looks like
        print("Loading file..")
        counts = [0, 0]
        with open("temp_local_copy") as f:
            for x in f.read():
                counts[ord(x) - ord(b'0')] += 1
        
        print("Results")
        print(counts)
    
    if __name__ == "__main__":
        main()
    

    此代码将一个 100mb 的“0”对象上传到 S3。然后,它使用相同的密钥开始上传 100mb 的“1”,当第二次上传完成 95% 时,它开始下载该 S3 对象。然后它会计算在下载的文件中有多少个“0”和“1”。

    使用最新版本的 Python 和 Boto3 运行此程序,由于网络条件,您的确切输出无疑会与我的不同,但这是我在测试运行时看到的:

    Uploading all zeroes..
    Done uploading all zeroes
    Uploading all ones..
    Starting download...
    Done uploading all ones
    Done with download
    Loading file..
    Results
    [83886080, 20971520]
    

    最后一行很重要。下载的文件大部分是“0”字节,但有 20mb 的“1”字节。意思是,尽管只执行了一次下载调用,但我得到了文件 v1 的一部分和 v2 的一部分。

    现在,在实践中,这不太可能发生,更何况如果你有更好的网络带宽,那么我会在这里运行一个普通的家庭互联网连接。 但它总是可能发生。如果您需要确保下载者永远不会看到这样的部分文件,您要么需要做一些事情,比如验证文件的哈希是否正确,或者我的偏好是每次使用不同的密钥上传,并有一些机制客户端来发现“最新”密钥,因此他们可以下载整个未更改的文件,即使在他们上传时上传完成。

    【讨论】:

      【解决方案2】:

      在新文件完全上传之前,读者只能看到旧文件。没有读取部分文件。

      【讨论】:

      • 您能提供一份证明文件吗?
      • 查看帖子正文。
      猜你喜欢
      • 1970-01-01
      • 2018-07-03
      • 1970-01-01
      • 2019-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-04
      • 2019-02-12
      相关资源
      最近更新 更多