xref: /aosp_15_r20/external/federated-compute/fcp/client/federated_protocol_util.cc (revision 14675a029014e728ec732f129a32e299b2da0601)
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