rav*_*avi 4 amazon-s3 amazon-web-services
我有一个 lambda (L1) 将文件 (100MB) 替换到 s3 位置 ( s3://bucket/folder/abc.json )。我有另一个 lambda(L2、L3)同时读取同一文件,一个通过 golang api,另一个通过 Athena 查询。s3 存储桶/文件夹没有版本控制。
问题是:lambdas L2、L3 是否会读取文件的旧副本,直到新文件上传为止?或者它是否读取正在上传的部分文件?如果是后者,那么如何确保 L2、L3 仅在完全上传时读取文件?
Amazon S3 现在是强一致的。这意味着一旦您上传对象,所有读取该对象的人都保证获得该对象的更新版本。
从表面上看,这听起来像是保证您的问题是“是的,所有客户端都将获得文件的旧版本或新版本”。事实仍然比这更模糊。
在幕后,许多 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()
Run Code Online (Sandbox Code Playgroud)
此代码将一个 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]
Run Code Online (Sandbox Code Playgroud)
最后一行很重要。下载的文件大部分是“0”字节,但也有 20mb 的“1”字节。这意味着,尽管只执行了一次下载调用,但我还是获得了文件 v1 的某些部分和 v2 的某些部分。
现在,在实践中,这种情况不太可能发生,如果你有更好的网络带宽,那么我在普通的家庭互联网连接上就更是如此。但它总是有可能发生。如果您需要确保下载者永远不会看到这样的部分文件,您要么需要执行一些操作,例如验证文件的哈希值是否正确,或者我的偏好是每次使用不同的密钥上传,并有一些机制客户端发现“最新”密钥,以便他们可以下载整个未更改的文件,即使上传在上传时完成。
归档时间: |
|
查看次数: |
1709 次 |
最近记录: |