1 /*
2 * Copyright 2022 Google LLC
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 #include "fcp/client/federated_protocol_util.h"
17
18 #include <algorithm>
19 #include <string>
20
21 #include "google/protobuf/duration.pb.h"
22 #include "absl/random/random.h"
23 #include "absl/status/statusor.h"
24 #include "absl/time/clock.h"
25 #include "absl/time/time.h"
26 #include "fcp/base/monitoring.h"
27 #include "fcp/base/time_util.h"
28 #include "fcp/client/diag_codes.pb.h"
29 #include "fcp/client/log_manager.h"
30 #include "fcp/protos/federated_api.pb.h"
31
32 namespace fcp {
33 namespace client {
34
35 namespace {
36
37 // Takes the given minimum and maximum delays, and uniformly randomly
38 // chooses a delay in that range.
PickRetryDelayFromRange(absl::Duration min_delay,absl::Duration max_delay,absl::BitGen & bit_gen)39 absl::Duration PickRetryDelayFromRange(absl::Duration min_delay,
40 absl::Duration max_delay,
41 absl::BitGen& bit_gen) {
42 // Sanitize inputs (ensure min_delay is >= 0, and max_delay is >= min_delay).
43 min_delay = std::max(absl::ZeroDuration(), min_delay);
44 max_delay = std::max(max_delay, min_delay);
45
46 // Pick a value.
47 absl::Duration window_width = max_delay - min_delay;
48 double random = absl::Uniform(bit_gen, 0, 1.0);
49 return min_delay + (window_width * random);
50 }
51
52 } // namespace
53
PickRetryTimeFromRange(const::google::protobuf::Duration & min_delay,const::google::protobuf::Duration & max_delay,absl::BitGen & bit_gen)54 absl::Time PickRetryTimeFromRange(const ::google::protobuf::Duration& min_delay,
55 const ::google::protobuf::Duration& max_delay,
56 absl::BitGen& bit_gen) {
57 return absl::Now() +
58 PickRetryDelayFromRange(absl::Seconds(min_delay.seconds()) +
59 absl::Nanoseconds(min_delay.nanos()),
60 absl::Seconds(max_delay.seconds()) +
61 absl::Nanoseconds(max_delay.nanos()),
62 bit_gen);
63 }
64
65 ::google::internal::federatedml::v2::RetryWindow
GenerateRetryWindowFromTargetDelay(absl::Duration target_delay,double jitter_percent,absl::BitGen & bit_gen)66 GenerateRetryWindowFromTargetDelay(absl::Duration target_delay,
67 double jitter_percent,
68 absl::BitGen& bit_gen) {
69 // Sanitize the jitter_percent input, ensuring it's within [0.0 and 1.0]
70 jitter_percent = std::min(1.0, std::max(0.0, jitter_percent));
71 // Pick a retry delay from the target range.
72 absl::Duration retry_delay =
73 PickRetryDelayFromRange(target_delay * (1.0 - jitter_percent),
74 target_delay * (1.0 + jitter_percent), bit_gen);
75 ::google::internal::federatedml::v2::RetryWindow result;
76 *result.mutable_delay_min() = *result.mutable_delay_max() =
77 TimeUtil::ConvertAbslToProtoDuration(retry_delay);
78 return result;
79 }
80
81 ::google::internal::federatedml::v2::RetryWindow
GenerateRetryWindowFromRetryTime(absl::Time retry_time)82 GenerateRetryWindowFromRetryTime(absl::Time retry_time) {
83 // Convert the target retry time back to a duration, based on the current
84 // time. I.e. if at 09:50AM the retry window was received and the chosen
85 // target retry time was 11:00AM, and if it is now 09:55AM, then the
86 // calculated duration will be 1 hour and 5 minutes.
87 absl::Duration retry_delay = retry_time - absl::Now();
88 // If the target retry time has already passed, then use a zero-length
89 // duration.
90 retry_delay = std::max(absl::ZeroDuration(), retry_delay);
91
92 // Generate a RetryWindow with delay_min and delay_max both set to the same
93 // value.
94 ::google::internal::federatedml::v2::RetryWindow retry_window;
95 *retry_window.mutable_delay_min() = *retry_window.mutable_delay_max() =
96 TimeUtil::ConvertAbslToProtoDuration(retry_delay);
97 return retry_window;
98 }
99
ExtractTaskNameFromAggregationSessionId(const std::string & session_id,const std::string & population_name,LogManager & log_manager)100 std::string ExtractTaskNameFromAggregationSessionId(
101 const std::string& session_id, const std::string& population_name,
102 LogManager& log_manager) {
103 auto population_start = session_id.find(population_name + "/");
104 auto task_end = session_id.find('#');
105 if (population_start != 0 || task_end == std::string::npos ||
106 task_end <= population_name.length() + 1) {
107 log_manager.LogDiag(ProdDiagCode::OPSTATS_TASK_NAME_EXTRACTION_FAILED);
108 return session_id;
109 } else {
110 return session_id.substr(population_name.length() + 1,
111 task_end - population_name.length() - 1);
112 }
113 }
114
115 } // namespace client
116 } // namespace fcp
117