Skip to content

Commit e82f110

Browse files
authored
C++ migration: make C++ Datastore create gRPC objects (#1765)
Also introduce a new class, `GrpcConnection`, somewhat similar to the class of the same name in Web client, which does the bulk of the work making sure there is a valid gRPC channel/stub.
1 parent 7b43c61 commit e82f110

File tree

7 files changed

+432
-31
lines changed

7 files changed

+432
-31
lines changed

Firestore/Example/Firestore.xcodeproj/project.pbxproj

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
54511E8E209805F8005BD28F /* hashing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54511E8D209805F8005BD28F /* hashing_test.cc */; };
3838
5467FB01203E5717009C9584 /* FIRFirestoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */; };
3939
5467FB08203E6A44009C9584 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; };
40-
546854AA20A36867004BDBD5 /* datastore_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 546854A820A36867004BDBD5 /* datastore_test.cc */; };
40+
546854AA20A36867004BDBD5 /* datastore_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 546854A820A36867004BDBD5 /* datastore_test.mm */; };
4141
54740A571FC914BA00713A1A /* secure_random_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A531FC913E500713A1A /* secure_random_test.cc */; };
4242
54740A581FC914F000713A1A /* autoid_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A521FC913E500713A1A /* autoid_test.cc */; };
4343
54764FAF1FAA21B90085E60A /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; };
@@ -298,8 +298,8 @@
298298
132E3BB3D5C42282B4ACFB20 /* FSTLevelDBBenchmarkTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBBenchmarkTests.mm; sourceTree = "<group>"; };
299299
2A0CF41BA5AED6049B0BEB2C /* type_traits_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = type_traits_apple_test.mm; sourceTree = "<group>"; };
300300
2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
301-
332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = leveldb_util_test.cc; sourceTree = "<group>"; };
302-
3564F6872205C4F7001D5436 /* grpc_stream_tester.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = grpc_stream_tester.cc; sourceTree = "<group>"; };
301+
332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_util_test.cc; sourceTree = "<group>"; };
302+
3564F6872205C4F7001D5436 /* grpc_stream_tester.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = grpc_stream_tester.cc; sourceTree = "<group>"; };
303303
358C3B5FE573B1D60A4F7592 /* strerror_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = strerror_test.cc; sourceTree = "<group>"; };
304304
3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = "<group>"; };
305305
3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = "<group>"; };
@@ -311,7 +311,7 @@
311311
5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFirestoreTests.mm; sourceTree = "<group>"; };
312312
5467FB06203E6A44009C9584 /* app_testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = app_testing.h; sourceTree = "<group>"; };
313313
5467FB07203E6A44009C9584 /* app_testing.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = app_testing.mm; sourceTree = "<group>"; };
314-
546854A820A36867004BDBD5 /* datastore_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = datastore_test.cc; sourceTree = "<group>"; };
314+
546854A820A36867004BDBD5 /* datastore_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = datastore_test.mm; sourceTree = "<group>"; };
315315
54740A521FC913E500713A1A /* autoid_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = autoid_test.cc; sourceTree = "<group>"; };
316316
54740A531FC913E500713A1A /* secure_random_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = secure_random_test.cc; sourceTree = "<group>"; };
317317
54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTGoogleTestTests.mm; sourceTree = "<group>"; };
@@ -646,7 +646,7 @@
646646
546854A720A3681B004BDBD5 /* remote */ = {
647647
isa = PBXGroup;
648648
children = (
649-
546854A820A36867004BDBD5 /* datastore_test.cc */,
649+
546854A820A36867004BDBD5 /* datastore_test.mm */,
650650
B6D1B68420E2AB1A00B35856 /* exponential_backoff_test.cc */,
651651
B6BBE42F21262CF400C6A53E /* grpc_stream_test.cc */,
652652
61F72C5520BC48FD001A68CB /* serializer_test.cc */,
@@ -1877,13 +1877,12 @@
18771877
B6FB467D208E9D3C00554BA2 /* async_queue_test.cc in Sources */,
18781878
54740A581FC914F000713A1A /* autoid_test.cc in Sources */,
18791879
AB380D02201BC69F00D97691 /* bits_test.cc in Sources */,
1880-
B646EE6121224220001A1F18 /* buffered_writer_test.cc in Sources */,
18811880
618BBEA920B89AAC00B5BCE7 /* common.pb.cc in Sources */,
18821881
548DB929200D59F600E00ABC /* comparison_test.cc in Sources */,
18831882
ABC1D7DC2023A04B00BA84F0 /* credentials_provider_test.cc in Sources */,
18841883
ABE6637A201FA81900ED349A /* database_id_test.cc in Sources */,
18851884
AB38D93020236E21000A432D /* database_info_test.cc in Sources */,
1886-
546854AA20A36867004BDBD5 /* datastore_test.cc in Sources */,
1885+
546854AA20A36867004BDBD5 /* datastore_test.mm in Sources */,
18871886
618BBEAC20B89AAC00B5BCE7 /* document.pb.cc in Sources */,
18881887
B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */,
18891888
AB6B908420322E4D00CC290A /* document_test.cc in Sources */,

Firestore/core/src/firebase/firestore/remote/datastore.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,18 @@
1717
#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_DATASTORE_H_
1818
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_DATASTORE_H_
1919

20+
#include <memory>
21+
#include <string>
22+
23+
#include "Firestore/core/src/firebase/firestore/core/database_info.h"
24+
#include "Firestore/core/src/firebase/firestore/remote/grpc_connection.h"
25+
#include "Firestore/core/src/firebase/firestore/remote/grpc_stream.h"
26+
#include "Firestore/core/src/firebase/firestore/remote/grpc_stream_observer.h"
27+
#include "Firestore/core/src/firebase/firestore/util/async_queue.h"
28+
#include "Firestore/core/src/firebase/firestore/util/executor.h"
2029
#include "Firestore/core/src/firebase/firestore/util/status.h"
30+
#include "absl/strings/string_view.h"
31+
#include "grpcpp/completion_queue.h"
2132
#include "grpcpp/support/status.h"
2233

2334
namespace firebase {
@@ -26,12 +37,36 @@ namespace remote {
2637

2738
class Datastore {
2839
public:
29-
static util::Status ConvertStatus(grpc::Status grpc_error);
40+
Datastore(const core::DatabaseInfo& database_info,
41+
util::AsyncQueue* worker_queue);
42+
43+
void Shutdown();
44+
45+
std::unique_ptr<GrpcStream> CreateGrpcStream(absl::string_view rpc_name,
46+
absl::string_view token,
47+
GrpcStreamObserver* observer);
48+
49+
static util::Status ConvertStatus(grpc::Status from);
50+
51+
static std::string GetWhitelistedHeadersAsString(
52+
const GrpcStream::MetadataT& headers);
3053

3154
Datastore(const Datastore& other) = delete;
3255
Datastore(Datastore&& other) = delete;
3356
Datastore& operator=(const Datastore& other) = delete;
3457
Datastore& operator=(Datastore&& other) = delete;
58+
59+
private:
60+
void PollGrpcQueue();
61+
62+
static GrpcStream::MetadataT ExtractWhitelistedHeaders(
63+
const GrpcStream::MetadataT& headers);
64+
65+
// A separate executor dedicated to polling gRPC completion queue (which is
66+
// shared for all spawned `GrpcStream`s).
67+
std::unique_ptr<util::internal::Executor> rpc_executor_;
68+
grpc::CompletionQueue grpc_queue_;
69+
GrpcConnection grpc_connection_;
3570
};
3671

3772
} // namespace remote

Firestore/core/src/firebase/firestore/remote/datastore.mm

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,85 @@
1616

1717
#include "Firestore/core/src/firebase/firestore/remote/datastore.h"
1818

19+
#include <unordered_set>
20+
1921
#include "Firestore/core/include/firebase/firestore/firestore_errors.h"
22+
#include "Firestore/core/src/firebase/firestore/core/database_info.h"
23+
#include "Firestore/core/src/firebase/firestore/remote/grpc_completion.h"
24+
#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h"
2025
#include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
26+
#include "absl/memory/memory.h"
27+
#include "absl/strings/str_cat.h"
2128

2229
namespace firebase {
2330
namespace firestore {
2431
namespace remote {
2532

26-
util::Status Datastore::ConvertStatus(grpc::Status from) {
33+
using core::DatabaseInfo;
34+
using util::AsyncQueue;
35+
using util::Status;
36+
using util::internal::Executor;
37+
using util::internal::ExecutorLibdispatch;
38+
39+
namespace {
40+
41+
std::unique_ptr<Executor> CreateExecutor() {
42+
auto queue = dispatch_queue_create("com.google.firebase.firestore.rpc",
43+
DISPATCH_QUEUE_SERIAL);
44+
return absl::make_unique<ExecutorLibdispatch>(queue);
45+
}
46+
47+
std::string MakeString(grpc::string_ref grpc_str) {
48+
return {grpc_str.begin(), grpc_str.size()};
49+
}
50+
51+
absl::string_view MakeStringView(grpc::string_ref grpc_str) {
52+
return {grpc_str.begin(), grpc_str.size()};
53+
}
54+
55+
} // namespace
56+
57+
Datastore::Datastore(const DatabaseInfo &database_info,
58+
AsyncQueue *worker_queue)
59+
: grpc_connection_{database_info, worker_queue, &grpc_queue_},
60+
rpc_executor_{CreateExecutor()} {
61+
rpc_executor_->Execute([this] { PollGrpcQueue(); });
62+
}
63+
64+
void Datastore::Shutdown() {
65+
// `grpc::CompletionQueue::Next` will only return `false` once `Shutdown` has
66+
// been called and all submitted tags have been extracted. Without this call,
67+
// `rpc_executor_` will never finish.
68+
grpc_queue_.Shutdown();
69+
// Drain the executor to make sure it extracted all the operations from gRPC
70+
// completion queue.
71+
rpc_executor_->ExecuteBlocking([] {});
72+
}
73+
74+
void Datastore::PollGrpcQueue() {
75+
HARD_ASSERT(rpc_executor_->IsCurrentExecutor(),
76+
"PollGrpcQueue should only be called on the "
77+
"dedicated Datastore executor");
78+
79+
void *tag = nullptr;
80+
bool ok = false;
81+
while (grpc_queue_.Next(&tag, &ok)) {
82+
auto completion = static_cast<GrpcCompletion *>(tag);
83+
HARD_ASSERT(tag, "gRPC queue returned a null tag");
84+
completion->Complete(ok);
85+
}
86+
}
87+
88+
std::unique_ptr<GrpcStream> Datastore::CreateGrpcStream(
89+
absl::string_view rpc_name,
90+
absl::string_view token,
91+
GrpcStreamObserver *observer) {
92+
return grpc_connection_.CreateStream(token, rpc_name, observer);
93+
}
94+
95+
Status Datastore::ConvertStatus(grpc::Status from) {
2796
if (from.ok()) {
28-
return {};
97+
return Status::OK();
2998
}
3099

31100
grpc::StatusCode error_code = from.error_code();
@@ -36,6 +105,22 @@
36105
return {static_cast<FirestoreErrorCode>(error_code), from.error_message()};
37106
}
38107

108+
std::string Datastore::GetWhitelistedHeadersAsString(
109+
const GrpcStream::MetadataT &headers) {
110+
static std::unordered_set<std::string> whitelist = {
111+
"date", "x-google-backends", "x-google-netmon-label", "x-google-service",
112+
"x-google-gfe-request-trace"};
113+
114+
std::string result;
115+
for (const auto &kv : headers) {
116+
if (whitelist.find(MakeString(kv.first)) != whitelist.end()) {
117+
absl::StrAppend(&result, MakeStringView(kv.first), ": ",
118+
MakeStringView(kv.second), "\n");
119+
}
120+
}
121+
return result;
122+
}
123+
39124
} // namespace remote
40125
} // namespace firestore
41126
} // namespace firebase
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2018 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_GRPC_CONNECTION_H_
18+
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_GRPC_CONNECTION_H_
19+
20+
#include <memory>
21+
22+
#include "Firestore/core/src/firebase/firestore/core/database_info.h"
23+
#include "Firestore/core/src/firebase/firestore/remote/grpc_stream.h"
24+
#include "Firestore/core/src/firebase/firestore/remote/grpc_stream_observer.h"
25+
#include "absl/strings/string_view.h"
26+
#include "grpcpp/channel.h"
27+
#include "grpcpp/client_context.h"
28+
#include "grpcpp/completion_queue.h"
29+
#include "grpcpp/generic/generic_stub.h"
30+
31+
namespace firebase {
32+
namespace firestore {
33+
namespace remote {
34+
35+
// PORTING NOTE: this class has limited resemblance to `GrpcConnection` in Web
36+
// client. However, unlike Web client, it's not meant to hide different
37+
// implementations of a `Connection` under a single interface.
38+
39+
/**
40+
* Creates and owns gRPC objects (channel and stub) necessary to produce a
41+
* `GrpcStream`.
42+
*/
43+
class GrpcConnection {
44+
public:
45+
GrpcConnection(const core::DatabaseInfo& database_info,
46+
util::AsyncQueue* worker_queue,
47+
grpc::CompletionQueue* grpc_queue);
48+
49+
/**
50+
* Creates a stream to the given stream RPC endpoint. The resulting stream
51+
* needs to be `Start`ed before it can be used.
52+
*/
53+
// PORTING NOTE: unlike Web client, the created stream is not open and has to
54+
// be started manually.
55+
std::unique_ptr<GrpcStream> CreateStream(absl::string_view rpc_name,
56+
absl::string_view token,
57+
GrpcStreamObserver* observer);
58+
59+
private:
60+
std::unique_ptr<grpc::ClientContext> CreateContext(
61+
absl::string_view token) const;
62+
void EnsureActiveStub();
63+
64+
const core::DatabaseInfo* database_info_ = nullptr;
65+
util::AsyncQueue* worker_queue_ = nullptr;
66+
grpc::CompletionQueue* grpc_queue_ = nullptr;
67+
68+
std::shared_ptr<grpc::Channel> grpc_channel_;
69+
std::unique_ptr<grpc::GenericStub> grpc_stub_;
70+
};
71+
72+
} // namespace remote
73+
} // namespace firestore
74+
} // namespace firebase
75+
76+
#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_GRPC_CONNECTION_H_

0 commit comments

Comments
 (0)