In the previous post we saw, how to enable versioning on bucket and how each versions are represented in the bucket. Now we will take a look at reading different version of object.
Get_object without VersionId :
If you don’t specify VersionId in get_object call, then this will always return the latest object version. If you see the previous code snippet, last version uploaded has content = 'The God Father'
|
>>> s3c.get_object(Bucket=bucket, Key=objname)["Body"].read() 'The God Father' >>> |
And that’s exactly we see in get_object without version-id .
Get_object with VersionId :
If you give the VersionId (which was returned in the previous output) with get_object, this will return the respective object content.
|
>>> s3c.get_object(Bucket=bucket, Key=objname, VersionId="209394")["Body"].read() 'Star Wars' >>> s3c.get_object(Bucket=bucket, Key=objname, VersionId="209393")["Body"].read() 'The Dark Knight' >>> |
Get-object call without version-id will always return latest object-version, while to access specific object-version, just execute same API with version-id. Simple, isnt it.
Deleting Object :
Similar to get-object, you will have to specify version-id in delete-object API call in order to to delete the specific object version. If you don't specify Version-id in the delete call, then this will end up creating a delete marker on the object instead of deleting it.
What is Delete Marker :
Delete marker is technically a new object version. Delete call without VersionId will create a delete marker on the object, which becomes the latest version of the object. And any get_object call without versionID would fail. Existence of the delete marker means, latest version of the object is deleted . Though all the previous versions of the object stay intact.
|
>>> s3c.delete_object(Bucket=bucket, Key=objname) {u'VersionId': '209396', 'ResponseMetadata': {'HTTPStatusCode': 204, 'RetryAttempts': 0, 'HostId': '', 'RequestId': '15FCC095DD310205', 'HTTPHeaders': {'accept-ranges': 'bytes', 'x-amz-delete-marker': 'true', 'vary': 'Origin', 'server': 'NutanixS3', 'x-amz-request-id': '15FCC095DD310205', 'date': 'Mon, 16 Mar 2020 10:01:58 GMT', 'x-amz-version-id': '209396'}}, u'DeleteMarker': True} >>> |
You can see in the above output, delete_object API execution has returned VersionID='209396' and 'DeleteMarker'=True . You will not find the DeleteMarker field in previous code execution when we created multiple object versions (with valid data).
If you execute get_object API now, it will return an error :
|
>>> s3c.get_object(Bucket=bucket, Key=objname) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/anirudha.sonar/Library/Python/2.7/lib/python/site-packages/botocore/client.py", line 316, in _api_call return self._make_api_call(operation_name, kwargs) File "/Users/anirudha.sonar/Library/Python/2.7/lib/python/site-packages/botocore/client.py", line 626, in _make_api_call raise error_class(parsed_response, operation_name) botocore.errorfactory.NoSuchKey: An error occurred (NoSuchKey) when calling the GetObject operation: The specified key does not exist. >>> |
It just means, latest object version here is DeleteMarker and you can not read this object.
To get previous object, just specify VersionId :
|
>>> s3c.get_object(Bucket=bucket, Key=objname,VersionId="209395")["Body"].read() 'The God Father' >>>
|
You can delete delete marker by initiating another delete_object api with VersionId (of delete marker)
|
>>> s3c.delete_object(Bucket=bucket, Key=objname, VersionId='209396') {u'VersionId': '209396', 'ResponseMetadata': {'HTTPStatusCode': 204, 'RetryAttempts': 0, 'HostId': '', 'RequestId': '15FCC1C12785F8C1', 'HTTPHeaders': {'accept-ranges': 'bytes', 'vary': 'Origin', 'server': 'NutanixS3', 'x-amz-request-id': '15FCC1C12785F8C1', 'date': 'Mon, 16 Mar 2020 10:23:24 GMT', 'x-amz-version-id': '209396'}}} >>> |
Delete_object call without Versionid will create a new DeleteMarker with every API execution. So you may end up in having one to many delete markers on the same object.
Now execute get_object call :
|
>>> s3c.get_object(Bucket=bucket, Key=objname,VersionId="209395")["Body"].read() 'The God Father' >>> |
So here delete_object call has deleted the previously created DeleteMarker which made object-version='209395' as the latest version of the object. And get_object call without VersionId always returns latest version of the object, hence above get_object returned content = 'The God Father'
Now lets delete object-version='209395'
|
>>> s3c.delete_object(Bucket=bucket, Key=objname,VersionId='209395') {u'VersionId': '209395', 'ResponseMetadata': {'HTTPStatusCode': 204, 'RetryAttempts': 0, 'HostId': '', 'RequestId': '15FCC116FDBFB779', 'HTTPHeaders': {'accept-ranges': 'bytes', 'vary': 'Origin', 'server': 'NutanixS3', 'x-amz-request-id': '15FCC116FDBFB779', 'date': 'Mon, 16 Mar 2020 10:11:13 GMT', 'x-amz-version-id': '209395'}}} >>> |
This will permanently delete a specific version of the object. In case if this was the latest version, then the previously created version becomes the latest version . To confirm this , if I initiate get_object call now, I should get content= “Star Wars” (Refer : From above code and output, this is how object version to content mapping look section for versioned-content mapping)