avoid unnecessary KMS requests during single-part PUT (#9220)
This commit fixes a performance issue caused by too many calls to the external KMS - i.e. for single-part PUT requests. In general, the issue is caused by a sub-optimal code structure. In particular, when the server encrypts an object it requests a new data encryption key from the KMS. With this key it does some key derivation and encrypts the object content and ETag. However, to behave S3-compatible the MinIO server has to return the plaintext ETag to the client in case SSE-S3. Therefore, the server code used to decrypt the (previously encrypted) ETag again by requesting the data encryption key (KMS decrypt API) from the KMS. This leads to 2 KMS API calls (1 generate key and 1 decrypt key) per PUT operation - while only one KMS call is necessary. This commit fixes this by fetching a data key only once from the KMS and keeping the derived object encryption key around (for the lifetime of the request). This leads to a significant performance improvement w.r.t. to PUT workloads: ``` Operation: PUT Operations: 161 -> 239 Duration: 28s -> 29s * Average: +47.56% (+25.8 MiB/s) throughput, +47.56% (+2.6) obj/s * Fastest: +55.49% (+34.5 MiB/s) throughput, +55.49% (+3.5) obj/s * 50% Median: +58.24% (+32.8 MiB/s) throughput, +58.24% (+3.3) obj/s * Slowest: +1.83% (+0.6 MiB/s) throughput, +1.83% (+0.1) obj/s ```
This commit is contained in:
committed by
GitHub
parent
cea078a593
commit
db41953618
@@ -256,6 +256,78 @@ func TestDecryptObjectInfo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var decryptETagTests = []struct {
|
||||
ObjectKey crypto.ObjectKey
|
||||
ObjectInfo ObjectInfo
|
||||
ShouldFail bool
|
||||
ETag string
|
||||
}{
|
||||
{
|
||||
ObjectKey: [32]byte{},
|
||||
ObjectInfo: ObjectInfo{ETag: "20000f00f27834c9a2654927546df57f9e998187496394d4ee80f3d9978f85f3c7d81f72600cdbe03d80dc5a13d69354"},
|
||||
ETag: "8ad3fe6b84bf38489e95c701c84355b6",
|
||||
},
|
||||
{
|
||||
ObjectKey: [32]byte{},
|
||||
ObjectInfo: ObjectInfo{ETag: "20000f00f27834c9a2654927546df57f9e998187496394d4ee80f3d9978f85f3c7d81f72600cdbe03d80dc5a13d6935"},
|
||||
ETag: "",
|
||||
ShouldFail: true, // ETag is not a valid hex value
|
||||
},
|
||||
{
|
||||
ObjectKey: [32]byte{},
|
||||
ObjectInfo: ObjectInfo{ETag: "00000f00f27834c9a2654927546df57f9e998187496394d4ee80f3d9978f85f3c7d81f72600cdbe03d80dc5a13d69354"},
|
||||
ETag: "",
|
||||
ShouldFail: true, // modified ETag
|
||||
},
|
||||
|
||||
// Special tests for ETags that end with a '-x'
|
||||
{
|
||||
ObjectKey: [32]byte{},
|
||||
ObjectInfo: ObjectInfo{ETag: "916516b396f0f4d4f2a0e7177557bec4-1"},
|
||||
ETag: "916516b396f0f4d4f2a0e7177557bec4-1",
|
||||
},
|
||||
{
|
||||
ObjectKey: [32]byte{},
|
||||
ObjectInfo: ObjectInfo{ETag: "916516b396f0f4d4f2a0e7177557bec4-738"},
|
||||
ETag: "916516b396f0f4d4f2a0e7177557bec4-738",
|
||||
},
|
||||
{
|
||||
ObjectKey: [32]byte{},
|
||||
ObjectInfo: ObjectInfo{ETag: "916516b396f0f4d4f2a0e7177557bec4-Q"},
|
||||
ETag: "",
|
||||
ShouldFail: true, // Q is not a number
|
||||
},
|
||||
{
|
||||
ObjectKey: [32]byte{},
|
||||
ObjectInfo: ObjectInfo{ETag: "16516b396f0f4d4f2a0e7177557bec4-1"},
|
||||
ETag: "",
|
||||
ShouldFail: true, // ETag prefix is not a valid hex value
|
||||
},
|
||||
{
|
||||
ObjectKey: [32]byte{},
|
||||
ObjectInfo: ObjectInfo{ETag: "16516b396f0f4d4f2a0e7177557bec4-1-2"},
|
||||
ETag: "",
|
||||
ShouldFail: true, // ETag contains multiple: -
|
||||
},
|
||||
}
|
||||
|
||||
func TestDecryptETag(t *testing.T) {
|
||||
for i, test := range decryptETagTests {
|
||||
etag, err := DecryptETag(test.ObjectKey, test.ObjectInfo)
|
||||
if err != nil && !test.ShouldFail {
|
||||
t.Fatalf("Test %d: should succeed but failed: %v", i, err)
|
||||
}
|
||||
if err == nil && test.ShouldFail {
|
||||
t.Fatalf("Test %d: should fail but succeeded", i)
|
||||
}
|
||||
if err == nil {
|
||||
if etag != test.ETag {
|
||||
t.Fatalf("Test %d: ETag mismatch: got %s - want %s", i, etag, test.ETag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for issue reproduced when getting the right encrypted
|
||||
// offset of the object.
|
||||
func TestGetDecryptedRange_Issue50(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user