Skip to content

Commit 7854242

Browse files
Logan Gorefacebook-github-bot
authored andcommitted
Use singleton per-region for S3Client (#384)
Summary: Pull Request resolved: #384 # What * Use a singleton for each region when constructing our S3Client instead of a _single_ singleton * This is kind of a follow-up to D36727729 (47182c6) # Why * Follow-up to S281873 - S3Client singleton breaks multi-region support in PCF IO NOTE: PCF should be owning this code in the long-term since it's out of PSI's scope, but since we owned the initial SEV, I took ownership of this follow-up. Differential Revision: D39174441 fbshipit-source-id: 73dd6d6c88e216da3f99573689ef4c4eaa7d16ed
1 parent 6fa554d commit 7854242

1 file changed

Lines changed: 36 additions & 2 deletions

File tree

fbpcf/io/cloud_util/S3Client.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,46 @@
77

88
#include "fbpcf/io/cloud_util/S3Client.h"
99

10+
#include <string>
11+
1012
#include <aws/core/Aws.h>
1113
#include <aws/s3/S3Client.h>
1214

15+
#include <folly/container/F14Map.h>
16+
#include <folly/Synchronized.h>
17+
1318
namespace fbpcf::cloudio {
1419
S3Client& S3Client::getInstance(const fbpcf::aws::S3ClientOption& option) {
15-
static S3Client s3Client(option);
16-
return s3Client;
20+
/* Due to previous problems, we create a Singleton instance of the S3Client,
21+
* but there's a catch: we need a distinct S3Client for each region, or we
22+
* run into other issues. For that reason, we store this map from string to
23+
* S3Client with the assumption that the keys are region names. Since region
24+
* is optional, we also allow for a default empty string region.
25+
* ***************************** NOT THREAD SAFE ****************************
26+
* NOTE: Significant refactoring is required to make this thread safe
27+
* Downstream usage wants a mutable reference, but a folly::Synchronized
28+
* RWLock will return a const ref to a reader, meaning it's hard to refactor.
29+
* Simply trying to use folly::Synchronized around the map isn't sufficient,
30+
* because we'll leak a reference to an object in the map which is unsafe.
31+
* ***************************** NOT THREAD SAFE ****************************
32+
*/
33+
static folly::Synchronized<folly::F14FastMap<std::string, S3Client>> m;
34+
35+
std::string defaultStr{};
36+
auto region = option.region.value_or(defaultStr);
37+
38+
m.withWLock([&](auto& clientMap) {
39+
if (clientMap.find(region) == clientMap.end()) {
40+
clientMap.at(region) = S3Client{option};
41+
}
42+
});
43+
44+
/* You may see this and think, "Hey, the NOT THREAD SAFE warning above is
45+
* outdated, it looks like we fixed it!", but you're wrong. This still does
46+
* not fully solve the problem. Because the downstream consumer takes a
47+
* mutable reference, there's no guarantee that this is thread safe. It's
48+
* better than nothing, but you still shouldn't fully trust this code.
49+
*/
50+
return m.wlock()->at(region);
1751
}
1852
} // namespace fbpcf::cloudio

0 commit comments

Comments
 (0)