├── .bazelrc ├── .bazelversion ├── .gitignore ├── .travis.yml ├── BUILD ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── WORKSPACE ├── include ├── aggregation_options.h ├── service_control_client.h └── service_control_client_factory.h ├── sample ├── BUILD └── transport │ ├── http_sample.cc │ ├── http_transport.cc │ └── http_transport.h ├── script └── setup ├── src ├── aggregator_interface.h ├── cache_removed_items_handler.h ├── check_aggregator_impl.cc ├── check_aggregator_impl.h ├── check_aggregator_impl_test.cc ├── mock_transport.h ├── operation_aggregator.cc ├── operation_aggregator.h ├── operation_aggregator_test.cc ├── quota_aggregator_impl.cc ├── quota_aggregator_impl.h ├── quota_aggregator_impl_test.cc ├── quota_operation_aggregator.cc ├── quota_operation_aggregator.h ├── quota_operation_aggregator_test.cc ├── report_aggregator_impl.cc ├── report_aggregator_impl.h ├── report_aggregator_impl_test.cc ├── service_control_client_factory_impl.h ├── service_control_client_impl.cc ├── service_control_client_impl.h ├── service_control_client_impl_quota_test.cc ├── service_control_client_impl_test.cc ├── signature.cc ├── signature.h └── signature_test.cc ├── test ├── mocks.h └── mocks_test.cc └── utils ├── distribution_helper.cc ├── distribution_helper.h ├── distribution_helper_test.cc ├── google_macros.h ├── md5.cc ├── md5.h ├── md5_test.cc ├── simple_lru_cache.h ├── simple_lru_cache_inl.h ├── simple_lru_cache_test.cc ├── status.h ├── status_test_util.h ├── stl_util.h └── thread.h /.bazelrc: -------------------------------------------------------------------------------- 1 | # Address sanitizer 2 | build:asan --strip=never 3 | build:asan --copt -fsanitize=address 4 | build:asan --copt -O1 5 | build:asan --copt -fno-omit-frame-pointer 6 | build:asan --linkopt -fsanitize=address 7 | 8 | # Thread sanitizer 9 | build:tsan --strip=never 10 | build:tsan --copt -fsanitize=thread 11 | build:tsan --copt -pie 12 | build:tsan --copt -O2 13 | build:tsan --copt -g 14 | build:tsan --copt -fno-omit-frame-pointer 15 | build:tsan --linkopt -fsanitize=thread 16 | build:tsan --linkopt -pie 17 | build:tsan --linkopt -ltsan 18 | 19 | # Release builds 20 | build:release -c opt 21 | 22 | # Add compile option for all C++ files 23 | build --cxxopt -Wnon-virtual-dtor 24 | build --cxxopt -Wformat 25 | build --cxxopt -Wformat-security 26 | build --action_env=CC=clang-13 27 | build --action_env=CXX=clang++-13 28 | build --cxxopt=-std=c++17 --host_cxxopt=-std=c++17 29 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 4.1.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /bazel-* 3 | .idea/* 4 | CMakeLists.txt 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Adding sudo:required such that Travis doesn't use containers; otherwise 2 | # Bazel's sandbox execution will fail. The alternative is to disable Bazel's 3 | # sandbox execution using "--genrule_strategy=standalone", 4 | # "--spawn_strategy=standalone" and --test_strategy=standalone. 5 | sudo: required 6 | 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | packages: 12 | - gcc-4.8 13 | - g++-4.8 14 | - wget 15 | # Package list from http://bazel.io/docs/install.html 16 | - oracle-java8-installer 17 | - pkg-config 18 | - zip 19 | - zlib1g-dev 20 | 21 | install: 22 | - export JAVA_HOME=/usr/lib/jvm/java-8-oracle 23 | - export CC=gcc-4.8 24 | - export CXX=g++-4.8 25 | - wget 'https://github.com/bazelbuild/bazel/releases/download/0.3.1/bazel-0.3.1-installer-linux-x86_64.sh' -O ./bazel-installer.sh 26 | - chmod +x ./bazel-installer.sh 27 | - ./bazel-installer.sh --user 28 | 29 | script: 30 | - script/setup 31 | - bazel test :all --test_output=errors 32 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | licenses(["notice"]) 2 | 3 | exports_files(["LICENSE"]) 4 | 5 | cc_library( 6 | name = "distribution_helper_lib", 7 | srcs = ["utils/distribution_helper.cc"], 8 | hdrs = ["utils/distribution_helper.h"], 9 | visibility = ["//visibility:public"], 10 | deps = [ 11 | "@googleapis_git//google/api/servicecontrol/v1:servicecontrol_cc_proto", 12 | ], 13 | ) 14 | 15 | cc_library( 16 | name = "service_control_client_lib", 17 | srcs = [ 18 | "src/aggregator_interface.h", 19 | "src/cache_removed_items_handler.h", 20 | "src/check_aggregator_impl.cc", 21 | "src/check_aggregator_impl.h", 22 | "src/operation_aggregator.cc", 23 | "src/operation_aggregator.h", 24 | "src/quota_aggregator_impl.cc", 25 | "src/quota_aggregator_impl.h", 26 | "src/quota_operation_aggregator.cc", 27 | "src/quota_operation_aggregator.h", 28 | "src/report_aggregator_impl.cc", 29 | "src/report_aggregator_impl.h", 30 | "src/service_control_client_factory_impl.h", 31 | "src/service_control_client_impl.cc", 32 | "src/service_control_client_impl.h", 33 | "src/signature.cc", 34 | "src/signature.h", 35 | "utils/distribution_helper.cc", 36 | "utils/google_macros.h", 37 | "utils/md5.cc", 38 | "utils/md5.h", 39 | "utils/status_test_util.h", 40 | "utils/stl_util.h", 41 | "utils/thread.h", 42 | ], 43 | hdrs = [ 44 | "include/aggregation_options.h", 45 | "include/service_control_client.h", 46 | "include/service_control_client_factory.h", 47 | "utils/distribution_helper.h", 48 | "utils/simple_lru_cache.h", 49 | "utils/simple_lru_cache_inl.h", 50 | "utils/status.h", 51 | ], 52 | # A hack to use this BUILD as part of other projects. 53 | # The other projects will add this module as third_party/service-control-client-cxx 54 | copts = ["-Ithird_party/service-control-client-cxx"], 55 | visibility = ["//visibility:public"], 56 | deps = [ 57 | "@boringssl//:crypto", 58 | "@googleapis_git//google/api:metric_cc_proto", 59 | "@googleapis_git//google/api/servicecontrol/v1:servicecontrol_cc_proto", 60 | ], 61 | ) 62 | 63 | cc_library( 64 | name = "simple_lru_cache", 65 | srcs = ["utils/google_macros.h"], 66 | hdrs = [ 67 | "utils/simple_lru_cache.h", 68 | "utils/simple_lru_cache_inl.h", 69 | ], 70 | visibility = ["//visibility:public"], 71 | ) 72 | 73 | cc_library( 74 | name = "mocks_lib", 75 | testonly = 1, 76 | hdrs = ["test/mocks.h"], 77 | deps = [ 78 | ":service_control_client_lib", 79 | "@googletest_git//:gtest", 80 | ], 81 | ) 82 | 83 | cc_test( 84 | name = "quota_aggregator_impl_test", 85 | size = "small", 86 | srcs = ["src/quota_aggregator_impl_test.cc"], 87 | deps = [ 88 | ":service_control_client_lib", 89 | "@com_google_protobuf//:protobuf", 90 | "@googletest_git//:gtest_main", 91 | ], 92 | ) 93 | 94 | cc_test( 95 | name = "quota_operation_aggregator_test", 96 | size = "small", 97 | srcs = ["src/quota_operation_aggregator_test.cc"], 98 | linkopts = ["-lm"], 99 | deps = [ 100 | ":service_control_client_lib", 101 | "@com_google_protobuf//:protobuf", 102 | "@googletest_git//:gtest_main", 103 | ], 104 | ) 105 | 106 | cc_test( 107 | name = "distribution_helper_test", 108 | size = "small", 109 | srcs = ["utils/distribution_helper_test.cc"], 110 | deps = [ 111 | ":distribution_helper_lib", 112 | "@com_google_protobuf//:protobuf", 113 | "@googletest_git//:gtest_main", 114 | ], 115 | ) 116 | 117 | cc_test( 118 | name = "md5_test", 119 | size = "small", 120 | srcs = ["utils/md5_test.cc"], 121 | deps = [ 122 | ":service_control_client_lib", 123 | "@com_google_protobuf//:protobuf", 124 | "@googletest_git//:gtest_main", 125 | ], 126 | ) 127 | 128 | cc_test( 129 | name = "operation_aggregator_test", 130 | size = "small", 131 | srcs = ["src/operation_aggregator_test.cc"], 132 | linkopts = ["-lm"], 133 | deps = [ 134 | ":service_control_client_lib", 135 | "@com_google_protobuf//:protobuf", 136 | "@googletest_git//:gtest_main", 137 | ], 138 | ) 139 | 140 | cc_test( 141 | name = "report_aggregator_impl_test", 142 | size = "small", 143 | srcs = ["src/report_aggregator_impl_test.cc"], 144 | deps = [ 145 | ":service_control_client_lib", 146 | "@com_google_protobuf//:protobuf", 147 | "@googletest_git//:gtest_main", 148 | ], 149 | ) 150 | 151 | cc_test( 152 | name = "service_control_client_impl_test", 153 | size = "small", 154 | srcs = [ 155 | "src/mock_transport.h", 156 | "src/service_control_client_impl_test.cc", 157 | ], 158 | linkopts = ["-lm"], 159 | deps = [ 160 | ":service_control_client_lib", 161 | "@com_google_protobuf//:protobuf", 162 | "@googletest_git//:gtest_main", 163 | ], 164 | ) 165 | 166 | cc_test( 167 | name = "service_control_client_impl_quota_test", 168 | size = "small", 169 | srcs = [ 170 | "src/mock_transport.h", 171 | "src/service_control_client_impl_quota_test.cc", 172 | ], 173 | linkopts = ["-lm"], 174 | deps = [ 175 | ":service_control_client_lib", 176 | "@com_google_protobuf//:protobuf", 177 | "@googletest_git//:gtest_main", 178 | ], 179 | ) 180 | 181 | cc_test( 182 | name = "signature_test", 183 | size = "small", 184 | srcs = ["src/signature_test.cc"], 185 | deps = [ 186 | ":service_control_client_lib", 187 | "@googletest_git//:gtest_main", 188 | ], 189 | ) 190 | 191 | cc_test( 192 | name = "check_aggregator_impl_test", 193 | size = "small", 194 | srcs = ["src/check_aggregator_impl_test.cc"], 195 | deps = [ 196 | ":service_control_client_lib", 197 | "@com_google_protobuf//:protobuf", 198 | "@googletest_git//:gtest_main", 199 | ], 200 | ) 201 | 202 | cc_test( 203 | name = "simple_lru_cache_test", 204 | size = "small", 205 | srcs = ["utils/simple_lru_cache_test.cc"], 206 | linkopts = [ 207 | "-lm", 208 | "-lpthread", 209 | ], 210 | deps = [ 211 | ":simple_lru_cache", 212 | "@googletest_git//:gtest_main", 213 | ], 214 | ) 215 | 216 | cc_test( 217 | name = "mocks_test", 218 | size = "small", 219 | srcs = ["test/mocks_test.cc"], 220 | deps = [ 221 | ":mocks_lib", 222 | "@googletest_git//:gtest_main", 223 | ], 224 | ) 225 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your patches! Before we can take them, we have to jump a 6 | couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement 9 | (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an 13 | [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html). 14 | 15 | * If you work for a company that wants to allow you to contribute your work, 16 | then you'll need to sign a 17 | [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html). 18 | 19 | Follow either of the two links above to access the appropriate CLA and 20 | instructions for how to sign and return it. Once we receive it, we'll be able 21 | to accept your pull requests. 22 | 23 | ***NOTE***: Only original source code from you and other people that have signed 24 | the CLA can be accepted into the main repository. This policy does not apply to 25 | [third_party](third_party/). 26 | 27 | ## Contributing A Patch 28 | 29 | 1. If you haven't already done so, sign a Contributor License Agreement 30 | (see details above). 31 | 2. Fork our repo, develop and test your code changes. 32 | 3. Submit a pull request. 33 | 34 | ## Include Tests & Documentation 35 | 36 | Please include sufficient tests and documentation with your change. We will not 37 | be able to accept pull requests that do not include sufficient tests and 38 | corresponding documentation changes. 39 | 40 | ## Testing your changes 41 | 42 | bazel test :all 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/cloudendpoints/service-control-client-cxx.svg?branch=master)](https://travis-ci.org/cloudendpoints/service-control-client-cxx) 2 | 3 | # The Service Control Client library for c/c++ # 4 | 5 | The service control client library provides C++ APIs for: 6 | 7 | * access control check on API key validation 8 | * reporting API related data for google cloud logging and google cloud 9 | monitoring 10 | 11 | It offers: 12 | 13 | * fast access control check through caching 14 | * high scalability by significantly reducing outgoing check and report requests 15 | through aggregation 16 | 17 | ## Getting Service Control Client library ## 18 | 19 | To download the service control client source code, clone the repository: 20 | 21 | # Clone the repository 22 | git clone https://github.com/cloudendpoints/service-control-client-cxx.git 23 | 24 | ## Repository Structure ## 25 | 26 | * [include](/include): The folder contains public headers. 27 | * [utils](/utils): The folder contains utility code. 28 | * [src](/src): The folder contains core source code. 29 | 30 | ## Setup, Build and Test ## 31 | 32 | Recommended workflow to setup, build and test service control client code: 33 | 34 | # Sync your git repository with the origin. 35 | git checkout master 36 | git pull origin --rebase 37 | 38 | # Setup git for remote push 39 | script/setup 40 | 41 | # Use Bazel to build 42 | bazel build :all 43 | 44 | # Use Bazel to test 45 | bazel test :all 46 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 16 | 17 | http_archive( 18 | name = "com_google_protobuf", 19 | sha256 = "209385d3c08252e320196b628584c8007f849f9ec8a26c2796a886345ee58bb6", 20 | strip_prefix = "protobuf-2dca62f7296e5b49d729f7384f975cecb38382a0", # 05/31/2023 21 | urls = ["https://github.com/protocolbuffers/protobuf/archive/2dca62f7296e5b49d729f7384f975cecb38382a0.tar.gz"], 22 | ) 23 | 24 | load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") 25 | 26 | protobuf_deps() 27 | 28 | http_archive( 29 | name = "googleapis_git", 30 | sha256 = "b165b0f397f143d2e09d22c51aa90028d24ac3b755a103688e7a49090993155f", 31 | strip_prefix = "googleapis-736857e7a655eea72322e078b1988bd0d25aae0f", # 10/19/2022 32 | url = "https://github.com/googleapis/googleapis/archive/736857e7a655eea72322e078b1988bd0d25aae0f.tar.gz", 33 | ) 34 | 35 | load("@googleapis_git//:repository_rules.bzl", "switched_rules_by_language") 36 | 37 | switched_rules_by_language( 38 | name = "com_google_googleapis_imports", 39 | cc = True, 40 | ) 41 | 42 | http_archive( 43 | name = "googletest_git", 44 | sha256 = "f9830bcfb68c8a2a689337c4ad6998d6849df9574451f338a3dde14ff1f3e381", 45 | strip_prefix = "googletest-23b2a3b1cf803999fb38175f6e9e038a4495c8a5", # 2/13/2020 46 | urls = ["https://github.com/google/googletest/archive/23b2a3b1cf803999fb38175f6e9e038a4495c8a5.tar.gz"], 47 | ) 48 | 49 | http_archive( 50 | name = "boringssl", 51 | sha256 = "4825306f702fa5cb76fd86c987a88c9bbb241e75f4d86dbb3714530ca73c1fb1", 52 | strip_prefix = "boringssl-8cb07520451f0dc454654f2da5cdecf0b806f823", 53 | urls = ["https://github.com/google/boringssl/archive/8cb07520451f0dc454654f2da5cdecf0b806f823.tar.gz"], 54 | ) 55 | 56 | # Required by com_google_protobuf 57 | http_archive( 58 | name = "rules_python", 59 | sha256 = "e5470e92a18aa51830db99a4d9c492cc613761d5bdb7131c04bd92b9834380f6", 60 | strip_prefix = "rules_python-4b84ad270387a7c439ebdccfd530e2339601ef27", # 8/2/2019 61 | urls = ["https://github.com/bazelbuild/rules_python/archive/4b84ad270387a7c439ebdccfd530e2339601ef27.tar.gz"], 62 | ) 63 | 64 | # Required by com_google_protobuf 65 | http_archive( 66 | name = "bazel_skylib", 67 | sha256 = "b6cddd8206d5d2953791398b0f025a3f3f3c997872943625529e7b30eba92e78", 68 | strip_prefix = "bazel-skylib-c6f6b5425b232baf5caecc3aae31d49d63ddec03", # 06/09/2021 69 | urls = ["https://github.com/bazelbuild/bazel-skylib/archive/c6f6b5425b232baf5caecc3aae31d49d63ddec03.tar.gz"], 70 | ) 71 | 72 | # Required by com_google_protobuf 73 | http_archive( 74 | name = "zlib", 75 | build_file = "@com_google_protobuf//:third_party/zlib.BUILD", 76 | sha256 = "629380c90a77b964d896ed37163f5c3a34f6e6d897311f1df2a7016355c45eff", 77 | strip_prefix = "zlib-1.2.11", 78 | urls = ["https://github.com/madler/zlib/archive/v1.2.11.tar.gz"], 79 | ) 80 | -------------------------------------------------------------------------------- /include/aggregation_options.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_AGGREGATOR_OPTIONS_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_AGGREGATOR_OPTIONS_H_ 18 | 19 | #include 20 | #include 21 | 22 | #include "google/api/metric.pb.h" 23 | 24 | namespace google { 25 | namespace service_control_client { 26 | 27 | typedef std::unordered_map metric_value_t; 28 | 29 | // Defines a map of a metric name to its metric kind. 30 | typedef std::unordered_map 32 | MetricKindMap; 33 | 34 | // Default cache size 35 | constexpr int kDefaultQuotaCacheSize = 10000; 36 | // Default refresh interval is 1 second. 37 | constexpr int kDefaultQuotaRefreshInMs = 1000; 38 | // Since supported rate-limiting window is per minute, it make sense 39 | // to expire quota cache items in 1 minute. 40 | constexpr int kDefaultQuotaExpirationInMS = 60000; 41 | 42 | struct QuotaAggregationOptions { 43 | QuotaAggregationOptions() 44 | : num_entries(kDefaultQuotaCacheSize), 45 | refresh_interval_ms(kDefaultQuotaRefreshInMs), 46 | expiration_interval_ms(kDefaultQuotaExpirationInMS) {} 47 | 48 | // Constructor. 49 | // cache_entries is the maximum number of cache entries that can be kept in 50 | // the aggregation cache. Cache is disabled when cache_entries <= 0. 51 | // refresh_interval_ms is the maximum milliseconds before an aggregated quota 52 | // request needs to send to remote server again. 53 | // expiration_interval_ms should be at lease 10 times bigger 54 | // than the rate limit service's refill time window. 55 | QuotaAggregationOptions( 56 | int cache_entries, int refresh_interval_ms, 57 | int expiration_interval_ms = kDefaultQuotaExpirationInMS) 58 | : num_entries(cache_entries), refresh_interval_ms(refresh_interval_ms), 59 | expiration_interval_ms(expiration_interval_ms) {} 60 | 61 | // Maximum number of cache entries kept in the aggregation cache. 62 | // Set to 0 will disable caching and aggregation. 63 | int num_entries; 64 | 65 | // The refresh interval in milliseconds when aggregated quota will be send to 66 | // the server. 67 | int refresh_interval_ms; 68 | 69 | // The expiration interval in milliseconds. Cached element will be dropped 70 | // when the last refresh time is older than expiration_interval_ms 71 | int expiration_interval_ms; 72 | }; 73 | 74 | // Options controlling check aggregation behavior. 75 | struct CheckAggregationOptions { 76 | // Default constructor. 77 | CheckAggregationOptions() 78 | : num_entries(10000), flush_interval_ms(500), expiration_ms(1000) {} 79 | 80 | // Constructor. 81 | // cache_entries is the maximum number of cache entries that can be kept in 82 | // the aggregation cache. Cache is disabled when cache_entries <= 0. 83 | // flush_cache_entry_interval_ms is the maximum milliseconds before an 84 | // aggregated check request needs to send to remote server again. 85 | // response_expiration_ms is the maximum milliseconds before a cached check 86 | // response is invalidated. We make sure that it is at least 87 | // flush_cache_entry_interval_ms + 1. 88 | CheckAggregationOptions(int cache_entries, int flush_cache_entry_interval_ms, 89 | int response_expiration_ms) 90 | : num_entries(cache_entries), 91 | flush_interval_ms(flush_cache_entry_interval_ms), 92 | expiration_ms(std::max(flush_cache_entry_interval_ms + 1, 93 | response_expiration_ms)) {} 94 | 95 | // Maximum number of cache entries kept in the aggregation cache. 96 | // Set to 0 will disable caching and aggregation. 97 | const int num_entries; 98 | 99 | // Maximum milliseconds before aggregated check requests are flushed to the 100 | // server. The flush is triggered by a check request. 101 | const int flush_interval_ms; 102 | 103 | // Maximum milliseconds before a cached check response should be deleted. The 104 | // deletion is triggered by a timer. This value must be larger than 105 | // flush_interval_ms. 106 | const int expiration_ms; 107 | }; 108 | 109 | // Options controlling report aggregation behavior. 110 | struct ReportAggregationOptions { 111 | // Default constructor. 112 | ReportAggregationOptions() : num_entries(10000), flush_interval_ms(1000) {} 113 | 114 | // Constructor. 115 | // cache_entries is the maximum number of cache entries that can be kept in 116 | // the aggregation cache. Cache is disabled when cache_entries <= 0. 117 | // flush_cache_entry_interval_ms is the maximum milliseconds before aggregated 118 | // report requests are flushed to the server. The cache entry is deleted after 119 | // the flush. 120 | ReportAggregationOptions(int cache_entries, int flush_cache_entry_interval_ms) 121 | : num_entries(cache_entries), 122 | flush_interval_ms(flush_cache_entry_interval_ms) {} 123 | 124 | // Maximum number of cache entries kept in the aggregation cache. 125 | // Set to 0 will disable caching and aggregation. 126 | const int num_entries; 127 | 128 | // Maximum milliseconds before aggregated report requests are flushed to the 129 | // server. The flush is triggered by a timer. 130 | const int flush_interval_ms; 131 | }; 132 | 133 | } // namespace service_control_client 134 | } // namespace google 135 | 136 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_AGGREGATOR_OPTIONS_H_ 137 | -------------------------------------------------------------------------------- /include/service_control_client_factory.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2021 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_SERVICE_CONTROL_CLIENT_FACTORY_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_SERVICE_CONTROL_CLIENT_FACTORY_H_ 18 | 19 | #include "service_control_client.h" 20 | 21 | namespace google { 22 | namespace service_control_client { 23 | 24 | // A factory to create ServiceControlClients. 25 | // It does not provide any additional functionality other than making code 26 | // testable/mockable via dependency injection. 27 | class ServiceControlClientFactory { 28 | public: 29 | virtual ~ServiceControlClientFactory() {} 30 | 31 | // Create a `ServiceControlClient` object. 32 | virtual std::unique_ptr 33 | CreateClient(const std::string &service_name, 34 | const std::string &service_config_id, 35 | ServiceControlClientOptions &options) const = 0; 36 | 37 | // ServiceControlClientFactory is neither copyable nor movable. 38 | ServiceControlClientFactory(const ServiceControlClientFactory &) = delete; 39 | ServiceControlClientFactory & 40 | operator=(const ServiceControlClientFactory &) = delete; 41 | 42 | protected: 43 | ServiceControlClientFactory() {} 44 | }; 45 | 46 | } // namespace service_control_client 47 | } // namespace google 48 | 49 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_SERVICE_CONTROL_CLIENT_FACTORY_H_ 50 | -------------------------------------------------------------------------------- /sample/BUILD: -------------------------------------------------------------------------------- 1 | licenses(["notice"]) 2 | 3 | cc_library( 4 | name = "http_transport", 5 | srcs = [ 6 | "transport/http_transport.cc", 7 | ], 8 | hdrs = [ 9 | "transport/http_transport.h", 10 | ], 11 | visibility = ["//visibility:public"], 12 | deps = [ 13 | "@//:service_control_client_lib", 14 | "@//proto:servicecontrol", 15 | ], 16 | ) 17 | 18 | cc_binary( 19 | name = "http_sample", 20 | srcs = [ 21 | "transport/http_sample.cc", 22 | ], 23 | visibility = ["//visibility:public"], 24 | linkopts = [ 25 | "-lcurl", 26 | ], 27 | deps = [ 28 | ":http_transport", 29 | "@//:service_control_client_lib", 30 | ], 31 | ) 32 | -------------------------------------------------------------------------------- /sample/transport/http_sample.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | #include 16 | #include 17 | #include 18 | #include "google/api/servicecontrol/v1/service_controller.pb.h" 19 | #include "google/protobuf/stubs/logging.h" 20 | #include "google/protobuf/stubs/logging.h" 21 | #include "google/protobuf/stubs/status.h" 22 | #include "google/protobuf/text_format.h" 23 | #include "include/service_control_client.h" 24 | #include "sample/transport/http_transport.h" 25 | 26 | using ::google::api::servicecontrol::v1::CheckRequest; 27 | using ::google::api::servicecontrol::v1::CheckResponse; 28 | using ::google::api::servicecontrol::v1::ReportRequest; 29 | using ::google::api::servicecontrol::v1::ReportResponse; 30 | using ::google::service_control_client::CheckAggregationOptions; 31 | using ::google::service_control_client::ReportAggregationOptions; 32 | using ::google::service_control_client::ServiceControlClient; 33 | using ::google::service_control_client::ServiceControlClientOptions; 34 | using ::google::service_control_client::TransportDoneFunc; 35 | using ::google::service_control_client::sample::transport::LibCurlTransport; 36 | using ::google::protobuf::TextFormat; 37 | 38 | const char kCheckRequest[] = R"( 39 | service_name: "echo-dot-esp-load-test.appspot.com" 40 | operation { 41 | operation_id: "abced" 42 | operation_name: "EchoGetMessageAuthed" 43 | consumer_id: "project:esp-load-test" 44 | start_time: { 45 | seconds: 1000 46 | nanos: 2000 47 | } 48 | end_time: { 49 | seconds: 3000 50 | nanos: 4000 51 | } 52 | } 53 | )"; 54 | 55 | const char kReportRequest[] = R"( 56 | service_name: "echo-dot-esp-load-test.appspot.com" 57 | operations: { 58 | operation_id: "operation-1" 59 | operation_name: "EchoGetMessageAuthed" 60 | consumer_id: "project:esp-load-test" 61 | start_time { 62 | seconds: 1000 63 | nanos: 2000 64 | } 65 | end_time { 66 | seconds: 3000 67 | nanos: 4000 68 | } 69 | } 70 | )"; 71 | 72 | void print_usage() { 73 | fprintf(stderr, 74 | "Missing Argument.\n" 75 | "Usage: http_sample server_url service_name auth_token.\n" 76 | "auth_token can be obtained by one of the followings: \n" 77 | "1) Fetching from GCP metadata server if this code is running " 78 | "inside a Google Cloud Platform VM. \n" 79 | "2) Generating every one hour with service account secret file.\n" 80 | " a) Create service account and download client-secret-file: \n" 81 | " Google Cloud Console -> IAM & ADMIN -> Service Accounts \n" 82 | " -> Create Service Account (with editor role)\n" 83 | " b) Generate auth token: \n" 84 | " gen-auth-token.sh\n" 85 | " -s client-secret-file\n" 86 | " -a " 87 | "https://servicecontrol.googleapis.com/" 88 | "google.api.servicecontrol.v1.ServiceController\n"); 89 | } 90 | 91 | int main(int argc, char** argv) { 92 | if (argc <= 3) { 93 | print_usage(); 94 | return 1; 95 | } 96 | 97 | std::string server_url = argv[1]; 98 | std::string service_name = argv[2]; 99 | std::string auth_token = argv[3]; 100 | 101 | CheckRequest check_request; 102 | ReportRequest report_request; 103 | CheckResponse check_response; 104 | ReportResponse report_response; 105 | ::google::protobuf::TextFormat::ParseFromString(kCheckRequest, 106 | &check_request); 107 | ::google::protobuf::TextFormat::ParseFromString(kReportRequest, 108 | &report_request); 109 | 110 | // Initialize the sample transport. 111 | LibCurlTransport* transport = 112 | new LibCurlTransport(server_url, service_name, auth_token); 113 | 114 | // Initialize service control client. 115 | std::unique_ptr client; 116 | // Both check cache and report cache has been disabled. So each check/report 117 | // call at client will use the transport to call remote. 118 | ServiceControlClientOptions options( 119 | CheckAggregationOptions(-1 /*entries*/, 1000 /* refresh_interval_ms */, 120 | 1000 /*flush_interval_ms*/), 121 | ReportAggregationOptions(-1 /*entries*/, 1000 /* refresh_interval_ms */)); 122 | 123 | options.check_transport = [transport](const CheckRequest& check_request, 124 | CheckResponse* check_response, 125 | TransportDoneFunc on_done) { 126 | transport->Check(check_request, check_response, on_done); 127 | }; 128 | options.report_transport = [transport](const ReportRequest& report_request, 129 | ReportResponse* report_response, 130 | TransportDoneFunc on_done) { 131 | transport->Report(report_request, report_response, on_done); 132 | }; 133 | 134 | client = CreateServiceControlClient(service_name, "", options); 135 | 136 | // Call Check. 137 | std::promise check_promise_status; 138 | std::future check_future_status = check_promise_status.get_future(); 139 | client->Check(check_request, &check_response, [&check_promise_status]( 140 | const Status& status) { 141 | std::cout << "status is :" << status.ToString() << std::endl; 142 | std::promise moved_promise(std::move(check_promise_status)); 143 | moved_promise.set_value(status); 144 | }); 145 | 146 | // Call Report. 147 | std::promise report_promise_status; 148 | std::future report_future_status = report_promise_status.get_future(); 149 | client->Report(report_request, &report_response, [&report_promise_status]( 150 | const Status& status) { 151 | std::cout << "status is :" << status.ToString() << std::endl; 152 | std::promise moved_promise(std::move(report_promise_status)); 153 | moved_promise.set_value(status); 154 | }); 155 | 156 | check_future_status.wait(); 157 | report_future_status.wait(); 158 | 159 | std::cout << "check response is:" << check_response.DebugString() 160 | << std::endl; 161 | std::cout << "report response is:" << report_response.DebugString() 162 | << std::endl; 163 | 164 | delete transport; 165 | return 0; 166 | } 167 | -------------------------------------------------------------------------------- /sample/transport/http_transport.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | #include "sample/transport/http_transport.h" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | using ::google::api::servicecontrol::v1::CheckRequest; 23 | using ::google::api::servicecontrol::v1::CheckResponse; 24 | using ::google::api::servicecontrol::v1::ReportRequest; 25 | using ::google::api::servicecontrol::v1::ReportResponse; 26 | 27 | 28 | namespace google { 29 | namespace service_control_client { 30 | namespace sample { 31 | namespace transport { 32 | namespace { 33 | 34 | Status ConvertHttpCodeToStatus(const long &http_code) { 35 | Status status; 36 | std::cout << "http_code is: " << http_code << std::endl; 37 | switch (http_code) { 38 | case 400: 39 | status = Status(StatusCode::kInvalidArgument, std::string("Bad Request.")); 40 | case 403: 41 | status = 42 | Status(StatusCode::kPermissionDenied, std::string("Permission Denied.")); 43 | case 404: 44 | status = Status(StatusCode::kNotFound, std::string("Not Found.")); 45 | case 409: 46 | status = Status(StatusCode::kAborted, std::string("Conflict.")); 47 | case 416: 48 | status = Status(StatusCode::kOutOfRange, 49 | std::string("Requested Range Not Satisfiable.")); 50 | case 429: 51 | status = 52 | Status(StatusCode::kResourceExhausted, std::string("Too Many Requests.")); 53 | case 499: 54 | status = Status(StatusCode::kCancelled, std::string("Client Closed Request.")); 55 | case 504: 56 | status = Status(StatusCode::kDeadlineExceeded, std::string("Gateway Timeout.")); 57 | case 501: 58 | status = Status(StatusCode::kUnimplemented, std::string("Not Implemented.")); 59 | case 503: 60 | status = Status(StatusCode::kUnavailable, std::string("Service Unavailable.")); 61 | case 401: 62 | status = Status(StatusCode::kUnauthenticated, std::string("Unauthorized.")); 63 | default: { 64 | if (http_code >= 200 && http_code < 300) { 65 | status = Status(StatusCode::kOk, std::string("OK.")); 66 | 67 | } else if (http_code >= 400 && http_code < 500) { 68 | status = 69 | Status(StatusCode::kFailedPrecondition, std::string("Client Error.")); 70 | } else if (http_code >= 500 && http_code < 600) { 71 | status = Status(StatusCode::kInternal, std::string("Server Error.")); 72 | } else 73 | status = Status(StatusCode::kUnknown, std::string("Unknown Error.")); 74 | } 75 | } 76 | return status; 77 | } 78 | 79 | static size_t ResultBodyCallback(void *data, size_t size, size_t nmemb, 80 | void *instance) { 81 | size_t data_len = size * nmemb; 82 | 83 | if (data_len > 0) { 84 | ((std::string *)instance)->append((char *)data, size * nmemb); 85 | } 86 | return data_len; 87 | } 88 | 89 | Status SendHttp(const std::string &url, const std::string &auth_header, 90 | const std::string &request_body, std::string *response_body) { 91 | CURL *curl = curl_easy_init(); 92 | curl_easy_setopt(curl, CURLOPT_URL, url.data()); 93 | 94 | struct curl_slist *list = NULL; 95 | list = curl_slist_append(list, "Content-Type: application/x-protobuf"); 96 | list = curl_slist_append(list, "X-GFE-SSL: yes"); 97 | 98 | list = curl_slist_append(list, auth_header.c_str()); 99 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); 100 | 101 | // 0L will not include headers in response body. 1L will include headers in 102 | // response body. 103 | curl_easy_setopt(curl, CURLOPT_HEADER, 0L); 104 | curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); 105 | 106 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 107 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 108 | curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); 109 | curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, 110 | "ALL:!aNULL:!LOW:!EXPORT:!SSLv2"); 111 | 112 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request_body.data()); 113 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ResultBodyCallback); 114 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, response_body); 115 | curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); 116 | 117 | CURLcode res = curl_easy_perform(curl); 118 | Status status = OkStatus(); 119 | if (res != CURLE_OK) { 120 | std::cout << "curl easy_perform() failed." << std::endl; 121 | long http_code = 0; 122 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); 123 | Status status = ConvertHttpCodeToStatus(http_code); 124 | } 125 | 126 | curl_easy_cleanup(curl); 127 | return status; 128 | } 129 | 130 | } // namespace 131 | 132 | void LibCurlTransport::Check( 133 | const ::google::api::servicecontrol::v1::CheckRequest &request, 134 | ::google::api::servicecontrol::v1::CheckResponse *response, 135 | TransportDoneFunc on_done) { 136 | std::string *request_body = new std::string(); 137 | request.SerializeToString(request_body); 138 | 139 | std::thread t([request_body, response, on_done, this]() { 140 | std::string response_body; 141 | Status status = SendHttp(this->check_url_, this->auth_token_header_, 142 | *request_body, &response_body); 143 | delete request_body; 144 | if (status.ok()) { 145 | if (!response->ParseFromString(response_body)) { 146 | status = Status(StatusCode::kInvalidArgument, 147 | std::string("Cannot parse response to proto.")); 148 | } else { 149 | std::cout << "response parsed from string: " << response->DebugString() 150 | << std::endl; 151 | } 152 | } 153 | on_done(status); 154 | 155 | }); 156 | t.detach(); 157 | } 158 | 159 | void LibCurlTransport::Report( 160 | const ::google::api::servicecontrol::v1::ReportRequest &request, 161 | ::google::api::servicecontrol::v1::ReportResponse *response, 162 | TransportDoneFunc on_done) { 163 | std::string *request_body = new std::string(); 164 | request.SerializeToString(request_body); 165 | 166 | std::thread t([request_body, response, on_done, this]() { 167 | std::string response_body; 168 | Status status = SendHttp(this->report_url_, this->auth_token_header_, 169 | *request_body, &response_body); 170 | delete request_body; 171 | if (status.ok()) { 172 | if (!response->ParseFromString(response_body)) { 173 | status = Status(StatusCode::kInvalidArgument, 174 | std::string("Cannot parse response to proto.")); 175 | } else { 176 | std::cout << "response parsed from string: " << response->DebugString() 177 | << std::endl; 178 | } 179 | } 180 | on_done(status); 181 | }); 182 | t.detach(); 183 | } 184 | 185 | } // namespace transport 186 | } // namespace sample 187 | } // namespace service_control_client 188 | } // namespace google 189 | -------------------------------------------------------------------------------- /sample/transport/http_transport.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | #ifndef SERVICE_CONTROL_CLIENT_CXX_SAMPLE_HTTP_TRANSPORT_H 16 | #define SERVICE_CONTROL_CLIENT_CXX_SAMPLE_HTTP_TRANSPORT_H 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "google/api/servicecontrol/v1/service_controller.pb.h" 26 | #include "google/protobuf/stubs/logging.h" 27 | #include "google/protobuf/stubs/status.h" 28 | #include "include/service_control_client.h" 29 | 30 | using ::google::api::servicecontrol::v1::CheckRequest; 31 | using ::google::api::servicecontrol::v1::CheckResponse; 32 | using ::google::api::servicecontrol::v1::ReportRequest; 33 | using ::google::api::servicecontrol::v1::ReportResponse; 34 | 35 | namespace google { 36 | namespace service_control_client { 37 | namespace sample { 38 | namespace transport { 39 | 40 | class LibCurlTransport { 41 | public: 42 | LibCurlTransport(std::string server_url, std::string service_name, 43 | std::string token) { 44 | check_url_ = server_url + "/v1/services/" + service_name + ":check"; 45 | report_url_ = server_url + "/v1/services/" + service_name + ":report"; 46 | std::stringstream ss; 47 | ss << "Authorization: Bearer " << token; 48 | auth_token_header_ = ss.str(); 49 | curl_global_init(CURL_GLOBAL_DEFAULT); 50 | } 51 | 52 | ~LibCurlTransport() { curl_global_cleanup(); }; 53 | 54 | void Check(const ::google::api::servicecontrol::v1::CheckRequest &request, 55 | ::google::api::servicecontrol::v1::CheckResponse *response, 56 | TransportDoneFunc on_done); 57 | 58 | void Report(const ::google::api::servicecontrol::v1::ReportRequest &request, 59 | ::google::api::servicecontrol::v1::ReportResponse *response, 60 | TransportDoneFunc on_done); 61 | 62 | private: 63 | std::string auth_token_header_; 64 | std::string check_url_; 65 | std::string report_url_; 66 | }; 67 | 68 | } // namespace transport 69 | } // namespace sample 70 | } // namespace service_control_client 71 | } // namespace google 72 | 73 | #endif // SERVICE_CONTROL_CLIENT_CXX_SAMPLE_HTTP_TRANSPORT_H 74 | -------------------------------------------------------------------------------- /script/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2016 Google Inc. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ROOT=$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)") 18 | 19 | # Set up the ESP submodules. 20 | git -C "${ROOT}" submodule update --init --recursive 21 | 22 | COMMIT_MSG='https://gerrit-review.googlesource.com/tools/hooks/commit-msg' 23 | 24 | function install_hook() { 25 | local GIT_DIR="$(git -C "${1}" rev-parse --git-dir)" 26 | curl -Lo "${GIT_DIR}/hooks/commit-msg" "${COMMIT_MSG}" \ 27 | && chmod +x "${GIT_DIR}/hooks/commit-msg" 28 | } 29 | 30 | install_hook "${ROOT}" 31 | 32 | for repo in .; do 33 | ORIGIN=$(git -C "${ROOT}/${repo}" config remote.origin.url) 34 | git -C "${ROOT}/${repo}" config remote.review.pushurl ${ORIGIN} 35 | git -C "${ROOT}/${repo}" config remote.review.push HEAD:refs/for/master 36 | git -C "${ROOT}/${repo}" config remote.draft.pushurl ${ORIGIN} 37 | git -C "${ROOT}/${repo}" config remote.draft.push HEAD:refs/drafts/master 38 | done -------------------------------------------------------------------------------- /src/aggregator_interface.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_AGGREGATOR_INTERFACE_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_AGGREGATOR_INTERFACE_H_ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "google/api/servicecontrol/v1/quota_controller.pb.h" 26 | #include "google/api/servicecontrol/v1/service_controller.pb.h" 27 | #include "include/aggregation_options.h" 28 | 29 | namespace google { 30 | namespace service_control_client { 31 | 32 | // Aggregate Service_Control Report requests. 33 | // This interface is thread safe. 34 | class ReportAggregator { 35 | public: 36 | // Flush callback can be called when calling any of member functions. 37 | // If the callback function is blocked, the called member function, such as 38 | // Report(), will be blocked too. It is recommended that the callback function 39 | // should be fast and non blocking. 40 | using FlushCallback = std::function; 42 | 43 | virtual ~ReportAggregator() {} 44 | 45 | // Sets the flush callback function. 46 | // The callback function must be light and fast. If it needs to make 47 | // a remote call, it must be non-blocking call. 48 | // It should NOT call into this object again from this callback. 49 | // It will cause dead-lock. 50 | virtual void SetFlushCallback(FlushCallback callback) = 0; 51 | 52 | // Adds a report request to cache 53 | virtual absl::Status 54 | Report(const ::google::api::servicecontrol::v1::ReportRequest &request) = 0; 55 | 56 | // When the next Flush() should be called. 57 | // Returns in ms from now, or -1 for never 58 | virtual int GetNextFlushInterval() = 0; 59 | 60 | // Flushes aggregated requests longer than flush_interval. 61 | // Called at time specified by GetNextFlushInterval(). 62 | virtual absl::Status Flush() = 0; 63 | 64 | // Flushes out aggregated report requests, clears all cache items. 65 | // Usually called at destructor. 66 | virtual absl::Status FlushAll() = 0; 67 | 68 | protected: 69 | ReportAggregator() {} 70 | }; 71 | 72 | class QuotaAggregator { 73 | public: 74 | using FlushCallback = std::function; 76 | virtual ~QuotaAggregator(){}; 77 | 78 | // Sets the flush callback function. 79 | // The callback function must be light and fast. If it needs to make 80 | // a remote call, it must be non-blocking call. 81 | // It should NOT call into this object again from this callback. 82 | // It will cause dead-lock. 83 | virtual void SetFlushCallback(FlushCallback callback) = 0; 84 | 85 | // If the quota could not be handled by the cache, returns NOT_FOUND, 86 | // caller has to send the request to service control. 87 | // Otherwise, returns OK and cached response. 88 | virtual absl::Status 89 | Quota(const ::google::api::servicecontrol::v1::AllocateQuotaRequest &request, 90 | ::google::api::servicecontrol::v1::AllocateQuotaResponse *response) = 0; 91 | 92 | // Caches a response from a remote Service Controller AllocateQuota call. 93 | virtual absl::Status 94 | CacheResponse(const std::string &request_signature, 95 | const ::google::api::servicecontrol::v1::AllocateQuotaResponse 96 | &response) = 0; 97 | 98 | // When the next Flush() should be called. 99 | // Returns in ms from now, or -1 for never 100 | virtual int GetNextFlushInterval() = 0; 101 | 102 | // Invalidates expired allocate quota resposnes. 103 | // Called at time specified by GetNextFlushInterval(). 104 | virtual absl::Status Flush() = 0; 105 | 106 | // Flushes out all cached quota responses; clears all cache items. 107 | // Usually called at destructor. 108 | virtual absl::Status FlushAll() = 0; 109 | 110 | protected: 111 | QuotaAggregator() {} 112 | }; 113 | 114 | // Aggregate Service_Control Check requests. 115 | // This interface is thread safe. 116 | class CheckAggregator { 117 | public: 118 | // Flush callback can be called when calling any of member functions. 119 | // If the callback function is blocked, the called member function, such as 120 | // Check(), will be blocked too. It is recommended that the callback function 121 | // should be fast and non blocking. 122 | using FlushCallback = std::function; 124 | 125 | virtual ~CheckAggregator() {} 126 | 127 | // Sets the flush callback function. 128 | // The callback function must be light and fast. If it needs to make 129 | // a remote call, it must be non-blocking call. 130 | // It should NOT call into this object again from this callback. 131 | // It will cause dead-lock. 132 | virtual void SetFlushCallback(FlushCallback callback) = 0; 133 | 134 | // If the check could not be handled by the cache, returns NOT_FOUND, 135 | // caller has to send the request to service control. 136 | // Otherwise, returns OK and cached response. 137 | virtual absl::Status 138 | Check(const ::google::api::servicecontrol::v1::CheckRequest &request, 139 | ::google::api::servicecontrol::v1::CheckResponse *response) = 0; 140 | 141 | // Caches a response from a remote Service Controller Check call. 142 | virtual absl::Status CacheResponse( 143 | const std::string &request_signature, 144 | const ::google::api::servicecontrol::v1::CheckResponse &response) = 0; 145 | 146 | // When the next Flush() should be called. 147 | // Returns in ms from now, or -1 for never 148 | virtual int GetNextFlushInterval() = 0; 149 | 150 | // Invalidates expired check resposnes. 151 | // Called at time specified by GetNextFlushInterval(). 152 | virtual absl::Status Flush() = 0; 153 | 154 | // Flushes out all cached check responses; clears all cache items. 155 | // Usually called at destructor. 156 | virtual absl::Status FlushAll() = 0; 157 | 158 | protected: 159 | CheckAggregator() {} 160 | }; 161 | 162 | // Creates a report aggregator. 163 | std::unique_ptr 164 | CreateReportAggregator(const std::string &service_name, 165 | const std::string &service_config_id, 166 | const ReportAggregationOptions &options, 167 | std::shared_ptr metric_kind); 168 | 169 | // Creates a check aggregator. 170 | std::unique_ptr 171 | CreateCheckAggregator(const std::string &service_name, 172 | const std::string &service_config_id, 173 | const CheckAggregationOptions &options, 174 | std::shared_ptr metric_kind); 175 | 176 | // Creates a quota aggregator. 177 | std::unique_ptr 178 | CreateAllocateQuotaAggregator(const std::string &service_name, 179 | const std::string &service_config_id, 180 | const QuotaAggregationOptions &options); 181 | 182 | } // namespace service_control_client 183 | } // namespace google 184 | 185 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_AGGREGATOR_INTERFACE_H_ 186 | -------------------------------------------------------------------------------- /src/cache_removed_items_handler.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_CACHE_REMOVED_ITEMS_HANDLER_H 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_CACHE_REMOVED_ITEMS_HANDLER_H 18 | 19 | #include "src/aggregator_interface.h" 20 | #include "utils/simple_lru_cache.h" 21 | #include "utils/simple_lru_cache_inl.h" 22 | #include "utils/thread.h" 23 | 24 | namespace google { 25 | namespace service_control_client { 26 | // If flush callback is called inside OnCacheEntryDelete(), the callback 27 | // function SHOULD not call any of AggregatorImpl functions, otherwise it 28 | // will cause deadlock. The reason is: OnCacheEntryDelete() is already holding 29 | // cache_mutex_ lock and all of CheckAggregator methods will try acquire 30 | // cache_mutex_ lock which will cause dead lock. 31 | // The solution is to buffer the removed items into a stack allocated vector 32 | // in OnCacheEntryDelete(). After cache_mutex lock is released, calls flush 33 | // callback for these removed items. 34 | // class CacheRemovedItemsHandler is designed to implement this solution. 35 | // Both CheckAggregator and ReportAggregator are derived from 36 | // CacheRemovedItemsHandler. CacheRemovedItemsHandler has a member variable 37 | // stack_buffer_ to point to stack allocated vector which can be used to insert 38 | // cache removed item in OnCacheEntryDelete(). Actual vector has to be allocated 39 | // from stack by each caller of cache operation functions. Swapper can be 40 | // used to set the vector pointer and reset it. Here is a typical usage of 41 | // this class: 42 | // ReportCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 43 | // MutexLock lock(cache_mutex_); 44 | // ReportCacheRemovedItemsHandler::StackBuffer::Swapper swapper(this, 45 | // &stack_buffer); 46 | // cache_mutex_ lock has to be in between of the instantiation of StackBuffer 47 | // and the instantiation ofSwapper. All cache operations (which may evict cache 48 | // items) need to be wrapped by this code pattern. 49 | template class CacheRemovedItemsHandler { 50 | public: 51 | CacheRemovedItemsHandler() : flush_callback_(NULL), stack_buffer_(NULL) {} 52 | 53 | virtual ~CacheRemovedItemsHandler() {} 54 | 55 | protected: 56 | // The callback function to flush out cache items. 57 | using InternalFlushCallback = std::function; 58 | 59 | // Sets the flush callback function. 60 | void InternalSetFlushCallback(InternalFlushCallback callback) { 61 | MutexLock lock(callback_mutex_); 62 | flush_callback_ = callback; 63 | } 64 | 65 | void AddRemovedItem(const RequestType &item) { 66 | if (stack_buffer_) { 67 | stack_buffer_->Add(item); 68 | } 69 | } 70 | 71 | // Checks if an new item can be merged into an old item. 72 | // Derived class will implement this: CheckRequest will never merge. 73 | // A ReportRequest can carry multiple operations, it can merge many 74 | // reuqests until number of operations reaches certain size. 75 | virtual bool MergeItem(const RequestType &new_item, RequestType *old_item) { 76 | return false; 77 | } 78 | 79 | // Class StackBuffer is designed to maintain the stack allocated vector which 80 | // can be used to insert cache removed items. This class has to be 81 | // instantiated at stack. It should be used outside of cache_mutex lock. 82 | class StackBuffer final { 83 | public: 84 | StackBuffer(CacheRemovedItemsHandler *handler) : handler_(handler) {} 85 | 86 | virtual ~StackBuffer() { 87 | for (const auto &request : items_) { 88 | handler_->FlushOut(request); 89 | } 90 | } 91 | 92 | void Add(const RequestType &item) { 93 | if (items_.empty() || 94 | !handler_->MergeItem(item, &items_[items_.size() - 1])) { 95 | items_.push_back(item); 96 | } 97 | } 98 | 99 | // Class Swapper is used to swap stack_buffer_ variable in the 100 | // CacheRemovedItemsHandle class. It should be used within cache_mutex lock. 101 | class Swapper final { 102 | public: 103 | Swapper(CacheRemovedItemsHandler *handler, StackBuffer *buffer) 104 | : handler_(handler) { 105 | handler_->stack_buffer_ = buffer; 106 | } 107 | 108 | virtual ~Swapper() { handler_->stack_buffer_ = NULL; } 109 | 110 | private: 111 | CacheRemovedItemsHandler *handler_; 112 | }; 113 | 114 | private: 115 | CacheRemovedItemsHandler *handler_; 116 | // A vector to cache store removed items. 117 | std::vector items_; 118 | }; 119 | 120 | private: 121 | // Mutex guarding the access of flush_callback_; 122 | Mutex callback_mutex_; 123 | 124 | // The callback function to flush out cache items. 125 | InternalFlushCallback flush_callback_; 126 | 127 | // The pointer points to the StackBuffer instance where allocated vector is 128 | // stored. It should only be set and reset by StackBuffer::Swapper. 129 | StackBuffer *stack_buffer_; 130 | 131 | void FlushOut(const RequestType &request) { 132 | MutexLock lock(callback_mutex_); 133 | if (flush_callback_) { 134 | flush_callback_(request); 135 | } 136 | } 137 | }; 138 | 139 | } // namespace service_control_client 140 | } // namespace google 141 | 142 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_CACHE_REMOVED_ITEMS_HANDLER_H 143 | -------------------------------------------------------------------------------- /src/check_aggregator_impl.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "src/check_aggregator_impl.h" 17 | 18 | #include "src/signature.h" 19 | 20 | using ::google::api::MetricDescriptor; 21 | using ::google::api::servicecontrol::v1::CheckRequest; 22 | using ::google::api::servicecontrol::v1::CheckResponse; 23 | using ::google::api::servicecontrol::v1::Operation; 24 | using ::google::service_control_client::SimpleCycleTimer; 25 | using std::string; 26 | 27 | namespace google { 28 | namespace service_control_client { 29 | 30 | void CheckAggregatorImpl::CacheElem::Aggregate( 31 | const CheckRequest& request, const MetricKindMap* metric_kinds) { 32 | if (operation_aggregator_ == NULL) { 33 | operation_aggregator_.reset( 34 | new OperationAggregator(request.operation(), metric_kinds)); 35 | } else { 36 | operation_aggregator_->MergeOperation(request.operation()); 37 | } 38 | } 39 | 40 | CheckRequest CheckAggregatorImpl::CacheElem::ReturnCheckRequestAndClear( 41 | const string& service_name, const std::string& service_config_id) { 42 | CheckRequest request; 43 | request.set_service_name(service_name); 44 | request.set_service_config_id(service_config_id); 45 | 46 | if (operation_aggregator_ != NULL) { 47 | *(request.mutable_operation()) = operation_aggregator_->ToOperationProto(); 48 | operation_aggregator_ = NULL; 49 | } 50 | return request; 51 | } 52 | 53 | CheckAggregatorImpl::CheckAggregatorImpl( 54 | const string& service_name, const std::string& service_config_id, 55 | const CheckAggregationOptions& options, 56 | std::shared_ptr metric_kinds) 57 | : service_name_(service_name), 58 | service_config_id_(service_config_id), 59 | options_(options), 60 | metric_kinds_(metric_kinds) { 61 | // Converts flush_interval_ms to Cycle used by SimpleCycleTimer. 62 | flush_interval_in_cycle_ = 63 | options_.flush_interval_ms * SimpleCycleTimer::Frequency() / 1000; 64 | 65 | if (options.num_entries > 0) { 66 | cache_.reset(new CheckCache( 67 | options.num_entries, std::bind(&CheckAggregatorImpl::OnCacheEntryDelete, 68 | this, std::placeholders::_1))); 69 | cache_->SetMaxIdleSeconds(options.expiration_ms / 1000.0); 70 | } 71 | } 72 | 73 | CheckAggregatorImpl::~CheckAggregatorImpl() { 74 | // FlushAll() will remove all cache items. For each removed item, it will call 75 | // flush_callback. At destructor, it is better not to call the callback. 76 | SetFlushCallback(NULL); 77 | (void)FlushAll(); 78 | } 79 | 80 | // Set the flush callback function. 81 | void CheckAggregatorImpl::SetFlushCallback(FlushCallback callback) { 82 | InternalSetFlushCallback(callback); 83 | } 84 | 85 | // Add a check request to cache 86 | absl::Status CheckAggregatorImpl::Check(const CheckRequest& request, 87 | CheckResponse* response) { 88 | if (request.service_name() != service_name_) { 89 | return absl::Status( 90 | absl::StatusCode::kInvalidArgument, 91 | (string("Invalid service name: ") + request.service_name() + 92 | string(" Expecting: ") + service_name_)); 93 | } 94 | if (!request.has_operation()) { 95 | return absl::Status(absl::StatusCode::kInvalidArgument, 96 | "operation field is required."); 97 | } 98 | if (request.operation().importance() != Operation::LOW || !cache_) { 99 | // By returning NO_FOUND, caller will send request to server. 100 | return absl::Status(absl::StatusCode::kNotFound, ""); 101 | } 102 | 103 | string request_signature = GenerateCheckRequestSignature(request); 104 | 105 | CheckCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 106 | MutexLock lock(cache_mutex_); 107 | CheckCacheRemovedItemsHandler::StackBuffer::Swapper swapper(this, 108 | &stack_buffer); 109 | 110 | CheckCache::ScopedLookup lookup(cache_.get(), request_signature); 111 | if (!lookup.Found()) { 112 | // By returning NO_FOUND, caller will send request to server. 113 | return absl::Status(absl::StatusCode::kNotFound, ""); 114 | } 115 | 116 | CacheElem* elem = lookup.value(); 117 | 118 | // If the cached check response has check errors, then we assume the new 119 | // request should fail as well and return the cached check response. 120 | // However, after the flush interval, the first check request will be send to 121 | // the server to refresh the check response. Other check requests still fail 122 | // with cached check response. 123 | // If the cached check response is a pass, then we assume the new request 124 | // should pass as well and return the cached response directly, besides 125 | // updating the quota info to be the same as requested. The requested tokens 126 | // are aggregated until flushed. 127 | // More details can be found in design doc go/simple-chemist-client. 128 | if (elem->check_response().check_errors_size() > 0) { 129 | if (ShouldFlush(*elem)) { 130 | // Pretend that we did not find, so we can force it into a check request 131 | // to the server. 132 | // 133 | // Setting last check to now to block more check requests to Chemist. 134 | elem->set_last_check_time(SimpleCycleTimer::Now()); 135 | // By returning NO_FOUND, caller will send request to server. 136 | return absl::Status(absl::StatusCode::kNotFound, ""); 137 | } else { 138 | // Use cached response. 139 | *response = elem->check_response(); 140 | return absl::OkStatus(); 141 | } 142 | } else { 143 | elem->Aggregate(request, metric_kinds_.get()); 144 | 145 | if (ShouldFlush(*elem)) { 146 | if (elem->is_flushing()) { 147 | ABSL_LOG(WARNING) << "Last refresh request was not completed yet."; 148 | } 149 | elem->set_is_flushing(true); 150 | // Setting last check to now to block more check requests to Chemist. 151 | elem->set_last_check_time(SimpleCycleTimer::Now()); 152 | // By returning NO_FOUND, caller will send request to server. 153 | return absl::Status(absl::StatusCode::kNotFound, ""); 154 | } 155 | 156 | *response = elem->check_response(); 157 | } 158 | // TODO(qiwzhang): supports quota 159 | // ScaleQuotaTokens(request, elem->quota_scale(), response); 160 | 161 | return absl::OkStatus(); 162 | } 163 | 164 | bool CheckAggregatorImpl::ShouldFlush(const CacheElem& elem) { 165 | int64_t age = SimpleCycleTimer::Now() - elem.last_check_time(); 166 | // TODO(chengliang): consider accumulated tokens as well. If the 167 | // accumulated number of tokens is larger than a threshold, we may also 168 | // decide to send the request. 169 | // 170 | // This will prevent sending more RPCs while there is an ongoing one most of 171 | // the time, except when there is a long RPC that exceeds the flush interval. 172 | return age >= flush_interval_in_cycle_; 173 | } 174 | 175 | absl::Status CheckAggregatorImpl::CacheResponse( 176 | const std::string& request_signature, const CheckResponse& response) { 177 | CheckCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 178 | MutexLock lock(cache_mutex_); 179 | CheckCacheRemovedItemsHandler::StackBuffer::Swapper swapper(this, 180 | &stack_buffer); 181 | if (cache_) { 182 | CheckCache::ScopedLookup lookup(cache_.get(), request_signature); 183 | 184 | int64_t now = SimpleCycleTimer::Now(); 185 | // TODO(qiwzhang): supports quota 186 | // int scale = GetQuotaScale(request, response); 187 | int quota_scale = 0; 188 | if (lookup.Found()) { 189 | lookup.value()->set_last_check_time(now); 190 | lookup.value()->set_check_response(response); 191 | lookup.value()->set_quota_scale(quota_scale); 192 | lookup.value()->set_is_flushing(false); 193 | } else { 194 | CacheElem* cache_elem = new CacheElem(response, now, quota_scale); 195 | cache_->Insert(request_signature, cache_elem, 1); 196 | } 197 | } 198 | 199 | return absl::OkStatus(); 200 | } 201 | 202 | // When the next Flush() should be called. 203 | // Flush() call remove expired response. 204 | int CheckAggregatorImpl::GetNextFlushInterval() { 205 | if (!cache_) return -1; 206 | return options_.expiration_ms; 207 | } 208 | 209 | // Flush aggregated requests whom are longer than flush_interval. 210 | // Called at time specified by GetNextFlushInterval(). 211 | absl::Status CheckAggregatorImpl::Flush() { 212 | CheckCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 213 | MutexLock lock(cache_mutex_); 214 | CheckCacheRemovedItemsHandler::StackBuffer::Swapper swapper(this, 215 | &stack_buffer); 216 | if (cache_) { 217 | cache_->RemoveExpiredEntries(); 218 | } 219 | 220 | return absl::OkStatus(); 221 | } 222 | 223 | void CheckAggregatorImpl::OnCacheEntryDelete(CacheElem* elem) { 224 | if (!elem->HasPendingCheckRequest()) { 225 | delete elem; 226 | return; 227 | } 228 | 229 | CheckRequest request; 230 | request = elem->ReturnCheckRequestAndClear(service_name_, service_config_id_); 231 | AddRemovedItem(request); 232 | delete elem; 233 | } 234 | 235 | // Flush out aggregated check requests, clear all cache items. 236 | // Usually called at destructor. 237 | absl::Status CheckAggregatorImpl::FlushAll() { 238 | CheckCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 239 | MutexLock lock(cache_mutex_); 240 | CheckCacheRemovedItemsHandler::StackBuffer::Swapper swapper(this, 241 | &stack_buffer); 242 | if (cache_) { 243 | cache_->RemoveAll(); 244 | } 245 | 246 | return absl::OkStatus(); 247 | } 248 | 249 | std::unique_ptr CreateCheckAggregator( 250 | const std::string& service_name, const std::string& service_config_id, 251 | const CheckAggregationOptions& options, 252 | std::shared_ptr metric_kind) { 253 | return std::unique_ptr(new CheckAggregatorImpl( 254 | service_name, service_config_id, options, metric_kind)); 255 | } 256 | 257 | } // namespace service_control_client 258 | } // namespace google 259 | -------------------------------------------------------------------------------- /src/check_aggregator_impl.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // Caches and aggregates check requests. 17 | 18 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_CHECK_AGGREGATOR_IMPL_H_ 19 | #define GOOGLE_SERVICE_CONTROL_CLIENT_CHECK_AGGREGATOR_IMPL_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "absl/status/status.h" 26 | #include "google/api/metric.pb.h" 27 | #include "google/api/servicecontrol/v1/operation.pb.h" 28 | #include "google/api/servicecontrol/v1/service_controller.pb.h" 29 | #include "src/aggregator_interface.h" 30 | #include "src/cache_removed_items_handler.h" 31 | #include "src/operation_aggregator.h" 32 | #include "utils/simple_lru_cache.h" 33 | #include "utils/simple_lru_cache_inl.h" 34 | #include "utils/thread.h" 35 | 36 | namespace google { 37 | namespace service_control_client { 38 | 39 | // Caches/Batches/aggregates check requests and sends them to the server. 40 | // Thread safe. 41 | // 42 | // Some typical data flows: 43 | // 44 | // Creates a new cache entry and use cached response: 45 | // 1) Calls Check(), and it returns NOT_FOUND, 46 | // 2) Callers send the request to server and get its response. 47 | // 3) Calls CacheResponse() to set the response to cache. 48 | // 4) The new Check() calls after will find the cached response, use it. 49 | // 5) If the request has quota info, it will be aggregated to the cached entry. 50 | // 51 | // Refreshes a cached entry after refresh interval: 52 | // 1) Calls Check(), found a cached response, 53 | // 2) If it passes refresh_interval, Check() returns NOT_FOUND, callers 54 | // will send the request to server. 55 | // 3) The new Check() calls after will use old response before the new response 56 | // arrives. 57 | // 4) Callers will set the new response by calling CacheResponse(). 58 | // 5) The new Check() calls after will use the new response. 59 | // 60 | // After a response is expired: 61 | // 1) During Flush() call, if a cached response is expired, it wil be flushed 62 | // out. If it has aggregated quota info, flush_callback will be called to 63 | // send the request to server. 64 | // 2) Callers need to send the request to server, and get its new response. 65 | // 3) The new response will be added to the cache by calling CacheResponse(). 66 | // 4) If there is not Check() called for that entry before it is expired again, 67 | // it will NOT have aggregated data when that entry is removed. It will not 68 | // send to flush_callback(). The item simply just got deleted. 69 | // 70 | // Object life management: 71 | // The callers of this object needs to make sure the object is still valid 72 | // before calling its methods. Specifically, callers may use async 73 | // transport to send request to server and pass an on_done() callback to be 74 | // called when response is received. If on_done() function is calling 75 | // CheckAggregator->CacheReponse() funtion, caller MUST make sure the 76 | // CacheAggregator object is still valid. 77 | 78 | typedef CacheRemovedItemsHandler< 79 | ::google::api::servicecontrol::v1::CheckRequest> 80 | CheckCacheRemovedItemsHandler; 81 | 82 | class CheckAggregatorImpl : public CheckAggregator, 83 | public CheckCacheRemovedItemsHandler { 84 | public: 85 | // Constructor. 86 | // Does not take ownership of metric_kinds and controller, which must outlive 87 | // this instance. 88 | CheckAggregatorImpl(const std::string &service_name, 89 | const std::string &service_config_id, 90 | const CheckAggregationOptions &options, 91 | std::shared_ptr metric_kind); 92 | 93 | virtual ~CheckAggregatorImpl(); 94 | 95 | // Sets the flush callback function. 96 | // It is called when a cache entry is expired and it has aggregated quota 97 | // in the request. The callback function needs to send the request to server, 98 | // calls CacheResponse() to set its response. 99 | virtual void SetFlushCallback(FlushCallback callback); 100 | 101 | // If the check could not be handled by the cache, returns NOT_FOUND, 102 | // caller has to send the request to service control server and call 103 | // CacheResponse() to set the response to the cache. 104 | // Otherwise, returns OK and cached response. 105 | virtual absl::Status 106 | Check(const ::google::api::servicecontrol::v1::CheckRequest &request, 107 | ::google::api::servicecontrol::v1::CheckResponse *response); 108 | 109 | // Caches a response from a remote Service Controller Check call. 110 | virtual absl::Status CacheResponse( 111 | const std::string &request_signature, 112 | const ::google::api::servicecontrol::v1::CheckResponse &response); 113 | 114 | // When the next Flush() should be called. 115 | // Returns in ms from now, or -1 for never 116 | virtual int GetNextFlushInterval(); 117 | 118 | // Flushes expired cache response entries. 119 | // Called at time specified by GetNextFlushInterval(). 120 | virtual absl::Status Flush(); 121 | 122 | // Flushes out all cache items. Usually called at destructor. 123 | virtual absl::Status FlushAll(); 124 | 125 | private: 126 | // Cache entry for aggregated check requests and previous check response. 127 | class CacheElem { 128 | public: 129 | CacheElem(const ::google::api::servicecontrol::v1::CheckResponse &response, 130 | const int64_t time, const int quota_scale) 131 | : check_response_(response), last_check_time_(time), 132 | quota_scale_(quota_scale), is_flushing_(false) {} 133 | 134 | // Aggregates the given request to this cache entry. 135 | void 136 | Aggregate(const ::google::api::servicecontrol::v1::CheckRequest &request, 137 | const MetricKindMap *metric_kinds); 138 | 139 | // Returns the aggregated CheckRequest and reset the cache entry. 140 | ::google::api::servicecontrol::v1::CheckRequest 141 | ReturnCheckRequestAndClear(const std::string &service_name, 142 | const std::string &service_config_id); 143 | 144 | bool HasPendingCheckRequest() const { 145 | return operation_aggregator_ != NULL; 146 | } 147 | 148 | // Setter for check response. 149 | inline void 150 | set_check_response(const ::google::api::servicecontrol::v1::CheckResponse 151 | &check_response) { 152 | check_response_ = check_response; 153 | } 154 | // Getter for check response. 155 | inline const ::google::api::servicecontrol::v1::CheckResponse & 156 | check_response() const { 157 | return check_response_; 158 | } 159 | 160 | // Setter for last check time. 161 | inline void set_last_check_time(const int64_t last_check_time) { 162 | last_check_time_ = last_check_time; 163 | } 164 | // Getter for last check time. 165 | inline const int64_t last_check_time() const { return last_check_time_; } 166 | 167 | // Setter for check response. 168 | inline void set_quota_scale(const int quota_scale) { 169 | quota_scale_ = quota_scale; 170 | } 171 | // Getter for check response. 172 | inline int quota_scale() const { return quota_scale_; } 173 | 174 | // Getter and Setter of is_flushing_; 175 | inline bool is_flushing() const { return is_flushing_; } 176 | inline void set_is_flushing(bool v) { is_flushing_ = v; } 177 | 178 | private: 179 | // Internal operation. 180 | std::unique_ptr operation_aggregator_; 181 | 182 | // The check response for the last check request. 183 | ::google::api::servicecontrol::v1::CheckResponse check_response_; 184 | // In general, this is the last time a check response is updated. 185 | // 186 | // During flush, we set it to be the request start time to prevent a next 187 | // check request from triggering another flush. Note that this prevention 188 | // works only during the flush interval, which means for long RPC, there 189 | // could be up to RPC_time/flush_interval ongoing check requests. 190 | int64_t last_check_time_; 191 | // Scale used to predict how much quota are charged. It is calculated 192 | // as the tokens charged in the last check response / requested tokens. 193 | // The predicated amount tokens consumed is then request tokens * scale. 194 | // This field is valid only when check_response has no check errors. 195 | int quota_scale_; 196 | 197 | // If true, is sending the request to server to get new response. 198 | bool is_flushing_; 199 | }; 200 | 201 | using CacheDeleter = std::function; 202 | // Key is the signature of the check request. Value is the CacheElem. 203 | // It is a LRU cache with MaxIdelTime as response_expiration_time. 204 | using CheckCache = 205 | SimpleLRUCacheWithDeleter; 206 | 207 | // Returns whether we should flush a cache entry. 208 | // If the aggregated check request is less than flush interval, no need to 209 | // flush. 210 | bool ShouldFlush(const CacheElem &elem); 211 | 212 | // Flushes the internal operation in the elem and delete the elem. The 213 | // response from the server is NOT cached. 214 | // Takes ownership of the elem. 215 | void OnCacheEntryDelete(CacheElem *elem); 216 | 217 | // The service name for this cache. 218 | const std::string service_name_; 219 | // The service config id for this cache. 220 | const std::string service_config_id_; 221 | 222 | // The check aggregation options. 223 | CheckAggregationOptions options_; 224 | 225 | // Metric kinds. Key is the metric name and value is the metric kind. 226 | // Defaults to DELTA if not specified. Not owned. 227 | std::shared_ptr metric_kinds_; 228 | 229 | // Mutex guarding the access of cache_; 230 | Mutex cache_mutex_; 231 | 232 | // The cache that maps from operation signature to an operation. 233 | // We don't calculate fine grained cost for cache entries, assign each 234 | // entry 1 cost unit. 235 | // Guarded by mutex_, except when compare against NULL. 236 | std::unique_ptr cache_; 237 | 238 | // flush interval in cycles. 239 | int64_t flush_interval_in_cycle_; 240 | 241 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CheckAggregatorImpl); 242 | }; 243 | 244 | } // namespace service_control_client 245 | } // namespace google 246 | 247 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_CHECK_AGGREGATOR_IMPL_H_ 248 | -------------------------------------------------------------------------------- /src/mock_transport.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_MOCK_TRANSPORT_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_MOCK_TRANSPORT_H_ 18 | 19 | #include 20 | 21 | #include "absl/status/status.h" 22 | #include "google/protobuf/text_format.h" 23 | #include "google/protobuf/util/message_differencer.h" 24 | #include "include/service_control_client.h" 25 | #include "utils/thread.h" 26 | #include "gmock/gmock.h" 27 | #include "gtest/gtest.h" 28 | 29 | using ::absl::CancelledError; 30 | using ::absl::InternalError; 31 | using ::absl::UnknownError; 32 | using ::google::api::servicecontrol::v1::AllocateQuotaRequest; 33 | using ::google::api::servicecontrol::v1::AllocateQuotaResponse; 34 | using ::google::api::servicecontrol::v1::CheckRequest; 35 | using ::google::api::servicecontrol::v1::CheckResponse; 36 | using ::google::api::servicecontrol::v1::Operation; 37 | using ::google::api::servicecontrol::v1::ReportRequest; 38 | using ::google::api::servicecontrol::v1::ReportResponse; 39 | using ::google::protobuf::TextFormat; 40 | using ::google::protobuf::util::MessageDifferencer; 41 | using std::string; 42 | using ::testing::_; 43 | using ::testing::Invoke; 44 | using ::testing::Mock; 45 | 46 | namespace google { 47 | namespace service_control_client { 48 | namespace { 49 | 50 | // A mocking class to mock CheckTransport interface. 51 | class MockCheckTransport { 52 | public: 53 | MOCK_METHOD3(Check, 54 | void(const CheckRequest &, CheckResponse *, TransportDoneFunc)); 55 | TransportCheckFunc GetFunc() { 56 | return [this](const CheckRequest &request, CheckResponse *response, 57 | TransportDoneFunc on_done) { 58 | this->Check(request, response, on_done); 59 | }; 60 | } 61 | 62 | MockCheckTransport() : check_response_(NULL) { 63 | // To avoid vector resize which will cause segmentation fault. 64 | on_done_vector_.reserve(100); 65 | } 66 | 67 | ~MockCheckTransport() {} 68 | 69 | // The done callback is stored in on_done_. It MUST be called later. 70 | void CheckWithStoredCallback(const CheckRequest &request, 71 | CheckResponse *response, 72 | TransportDoneFunc on_done) { 73 | check_request_ = request; 74 | if (check_response_) { 75 | *response = *check_response_; 76 | } 77 | on_done_vector_.push_back(on_done); 78 | } 79 | 80 | // The done callback is called right away (in place). 81 | void CheckWithInplaceCallback(const CheckRequest &request, 82 | CheckResponse *response, 83 | TransportDoneFunc on_done) { 84 | check_request_ = request; 85 | if (check_response_) { 86 | *response = *check_response_; 87 | } 88 | on_done(done_status_); 89 | } 90 | 91 | // Saved check_request from mocked Transport::Check() call. 92 | CheckRequest check_request_; 93 | // If not NULL, the check response to send for mocked Transport::Check() call. 94 | CheckResponse *check_response_; 95 | 96 | // saved on_done callback from either Transport::Check() or 97 | // Transport::Report(). 98 | std::vector on_done_vector_; 99 | // The status to send in on_done call back for Check() or Report(). 100 | absl::Status done_status_; 101 | }; 102 | 103 | // A mocking class to mock QuotaTransport interface. 104 | class MockQuotaTransport { 105 | public: 106 | MOCK_METHOD3(Quota, void(const AllocateQuotaRequest &, 107 | AllocateQuotaResponse *, TransportDoneFunc)); 108 | 109 | TransportQuotaFunc GetFunc() { 110 | return [this](const AllocateQuotaRequest &request, 111 | AllocateQuotaResponse *response, TransportDoneFunc on_done) { 112 | this->Quota(request, response, on_done); 113 | }; 114 | } 115 | 116 | MockQuotaTransport() : quota_response_(NULL) { 117 | // To avoid vector resize which will cause segmentation fault. 118 | on_done_vector_.reserve(100); 119 | } 120 | 121 | ~MockQuotaTransport() {} 122 | 123 | // The done callback is stored in on_done_. It MUST be called later. 124 | void AllocateQuotaWithStoredCallback(const AllocateQuotaRequest &request, 125 | AllocateQuotaResponse *response, 126 | TransportDoneFunc on_done) { 127 | quota_request_ = request; 128 | if (quota_response_) { 129 | *response = *quota_response_; 130 | } 131 | on_done_vector_.push_back(on_done); 132 | } 133 | 134 | // The done callback is called right away (in place). 135 | void AllocateQuotaWithInplaceCallback(const AllocateQuotaRequest &request, 136 | AllocateQuotaResponse *response, 137 | TransportDoneFunc on_done) { 138 | quota_request_ = request; 139 | if (quota_response_) { 140 | *response = *quota_response_; 141 | } 142 | on_done(done_status_); 143 | } 144 | 145 | // Saved quota_request from mocked Transport::Quota() call. 146 | AllocateQuotaRequest quota_request_; 147 | // If not NULL, the quota response to send for mocked Transport::Quota() call. 148 | AllocateQuotaResponse *quota_response_; 149 | 150 | // saved on_done callback from either Transport::Quota() or 151 | // Transport::Report(). 152 | std::vector on_done_vector_; 153 | // The status to send in on_done call back for Quota() or Report(). 154 | absl::Status done_status_; 155 | }; 156 | 157 | // A mocking class to mock ReportTransport interface. 158 | class MockReportTransport { 159 | public: 160 | MOCK_METHOD3(Report, void(const ReportRequest &, ReportResponse *, 161 | TransportDoneFunc)); 162 | TransportReportFunc GetFunc() { 163 | return [this](const ReportRequest &request, ReportResponse *response, 164 | TransportDoneFunc on_done) { 165 | this->Report(request, response, on_done); 166 | }; 167 | } 168 | 169 | MockReportTransport() : report_response_(NULL) { 170 | // To avoid vector resize which will cause segmentation fault. 171 | on_done_vector_.reserve(100); 172 | } 173 | 174 | ~MockReportTransport() {} 175 | 176 | // The done callback is stored in on_done_. It MUST be called later. 177 | void ReportWithStoredCallback(const ReportRequest &request, 178 | ReportResponse *response, 179 | TransportDoneFunc on_done) { 180 | report_request_ = request; 181 | if (report_response_) { 182 | *response = *report_response_; 183 | } 184 | on_done_vector_.push_back(on_done); 185 | } 186 | 187 | // The done callback is called right away (in place). 188 | void ReportWithInplaceCallback(const ReportRequest &request, 189 | ReportResponse *response, 190 | TransportDoneFunc on_done) { 191 | report_request_ = request; 192 | if (report_response_) { 193 | *response = *report_response_; 194 | } 195 | on_done(done_status_); 196 | } 197 | 198 | // Saved report_request from mocked Transport::Report() call. 199 | ReportRequest report_request_; 200 | // If not NULL, the report response to send for mocked Transport::Report() 201 | // call. 202 | ReportResponse *report_response_; 203 | 204 | // saved on_done callback from either Transport::Check() or 205 | // Transport::Report(). 206 | std::vector on_done_vector_; 207 | // The status to send in on_done call back for Check() or Report(). 208 | absl::Status done_status_; 209 | }; 210 | 211 | // A mocking class to mock Periodic_Timer interface. 212 | class MockPeriodicTimer { 213 | public: 214 | MOCK_METHOD2(StartTimer, 215 | std::unique_ptr(int, std::function)); 216 | PeriodicTimerCreateFunc GetFunc() { 217 | return 218 | [this](int interval_ms, 219 | std::function func) -> std::unique_ptr { 220 | return this->StartTimer(interval_ms, func); 221 | }; 222 | } 223 | 224 | class MockTimer : public PeriodicTimer { 225 | public: 226 | // Cancels the timer. 227 | MOCK_METHOD0(Stop, void()); 228 | }; 229 | 230 | std::unique_ptr MyStartTimer(int interval_ms, 231 | std::function callback) { 232 | interval_ms_ = interval_ms; 233 | callback_ = callback; 234 | return std::unique_ptr(new MockTimer); 235 | } 236 | 237 | int interval_ms_; 238 | std::function callback_; 239 | }; 240 | 241 | } // namespace 242 | 243 | } // namespace service_control_client 244 | } // namespace google 245 | 246 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_MOCK_TRANSPORT_H_ 247 | -------------------------------------------------------------------------------- /src/operation_aggregator.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "src/operation_aggregator.h" 17 | 18 | #include "src/signature.h" 19 | #include "utils/distribution_helper.h" 20 | #include "utils/stl_util.h" 21 | 22 | using google::api::MetricDescriptor; 23 | using google::api::servicecontrol::v1::MetricValue; 24 | using google::api::servicecontrol::v1::MetricValueSet; 25 | using google::api::servicecontrol::v1::Operation; 26 | using ::google::protobuf::Timestamp; 27 | using std::string; 28 | 29 | namespace google { 30 | namespace service_control_client { 31 | 32 | namespace { 33 | 34 | // Each logEntry is about 0.5 KB. LogEntries can only be appended. 35 | // Not to append too many logEntries in one operation in order to 36 | // limit the final report size. 37 | const int kMaxLogEntries = 100; 38 | 39 | // Returns whether timestamp a is before b or not. 40 | bool TimestampBefore(const Timestamp& a, const Timestamp& b) { 41 | return a.seconds() < b.seconds() || 42 | (a.seconds() == b.seconds() && a.nanos() < b.nanos()); 43 | } 44 | 45 | // Merges two metric values, with metric kind being Cumulative or 46 | // Gauge. 47 | // 48 | // New value will override old value, based on the end time. 49 | void MergeCumulativeOrGaugeMetricValue(const MetricValue& from, 50 | MetricValue* to) { 51 | if (TimestampBefore(from.end_time(), to->end_time())) return; 52 | 53 | *to = from; 54 | } 55 | 56 | // Merges two metric values, with metric kind being Delta. 57 | // 58 | // Time [from_start, from_end] and [to_start, to_end] will be merged to time 59 | // [min(from_start, to_start), max(from_end, to_end)]. It is OK to have gap or 60 | // overlap between the two time spans. 61 | // 62 | // For INT64/DOUBLE/MONEY/DISTRIBUTION, values will be added together, 63 | // except no change when the bucket options does not match. 64 | void MergeDeltaMetricValue(const MetricValue& from, MetricValue* to) { 65 | if (to->value_case() != from.value_case()) { 66 | ABSL_LOG(WARNING) << "Metric values are not compatible: " 67 | << from.DebugString() << ", " << to->DebugString(); 68 | return; 69 | } 70 | 71 | if (from.has_start_time()) { 72 | if (!to->has_start_time() || 73 | TimestampBefore(from.start_time(), to->start_time())) { 74 | *(to->mutable_start_time()) = from.start_time(); 75 | } 76 | } 77 | 78 | if (from.has_end_time()) { 79 | if (!to->has_end_time() || 80 | TimestampBefore(to->end_time(), from.end_time())) { 81 | *(to->mutable_end_time()) = from.end_time(); 82 | } 83 | } 84 | 85 | switch (to->value_case()) { 86 | case MetricValue::kInt64Value: 87 | to->set_int64_value(to->int64_value() + from.int64_value()); 88 | break; 89 | case MetricValue::kDoubleValue: 90 | to->set_double_value(to->double_value() + from.double_value()); 91 | break; 92 | case MetricValue::kDistributionValue: 93 | (void)DistributionHelper::Merge(from.distribution_value(), 94 | to->mutable_distribution_value()); 95 | break; 96 | default: 97 | ABSL_LOG(WARNING) << "Unknown metric kind for: " << to->DebugString(); 98 | break; 99 | } 100 | } 101 | 102 | // Merges one metric value into another. 103 | void MergeMetricValue(MetricDescriptor::MetricKind metric_kind, 104 | const MetricValue& from, MetricValue* to) { 105 | if (metric_kind == MetricDescriptor::DELTA) { 106 | MergeDeltaMetricValue(from, to); 107 | } else { 108 | MergeCumulativeOrGaugeMetricValue(from, to); 109 | } 110 | } 111 | 112 | } // namespace 113 | 114 | OperationAggregator::OperationAggregator( 115 | const Operation& operation, 116 | const std::unordered_map* 117 | metric_kinds) 118 | : operation_(operation), metric_kinds_(metric_kinds) { 119 | MergeMetricValueSets(operation); 120 | 121 | // Clear the metric value sets in operation_. 122 | operation_.clear_metric_value_sets(); 123 | } 124 | 125 | void OperationAggregator::MergeOperation(const Operation& operation) { 126 | if (operation.has_start_time()) { 127 | if (!operation_.has_start_time() || 128 | TimestampBefore(operation.start_time(), operation_.start_time())) { 129 | *(operation_.mutable_start_time()) = operation.start_time(); 130 | } 131 | } 132 | 133 | if (operation.has_end_time()) { 134 | if (!operation_.has_end_time() || 135 | TimestampBefore(operation_.end_time(), operation.end_time())) { 136 | *(operation_.mutable_end_time()) = operation.end_time(); 137 | } 138 | } 139 | 140 | MergeMetricValueSets(operation); 141 | MergeLogEntries(operation); 142 | } 143 | 144 | bool OperationAggregator::TooBig() const { 145 | return operation_.log_entries_size() >= kMaxLogEntries; 146 | } 147 | 148 | Operation OperationAggregator::ToOperationProto() const { 149 | Operation op(operation_); 150 | 151 | for (const auto& metric_value_set : metric_value_sets_) { 152 | MetricValueSet* set = op.add_metric_value_sets(); 153 | set->set_metric_name(metric_value_set.first); 154 | 155 | for (const auto& metric_value : metric_value_set.second) { 156 | *(set->add_metric_values()) = metric_value.second; 157 | } 158 | } 159 | 160 | return op; 161 | } 162 | 163 | void OperationAggregator::MergeLogEntries(const Operation& operation) { 164 | for (const auto& entry : operation.log_entries()) { 165 | *(operation_.add_log_entries()) = entry; 166 | } 167 | } 168 | 169 | void OperationAggregator::MergeMetricValueSets(const Operation& operation) { 170 | for (const auto& metric_value_set : operation.metric_value_sets()) { 171 | // Intentionally use the side effect of [] to add missing keys. 172 | std::unordered_map& metric_values = 173 | metric_value_sets_[metric_value_set.metric_name()]; 174 | 175 | MetricDescriptor::MetricKind metric_kind = MetricDescriptor::DELTA; 176 | if (metric_kinds_) { 177 | metric_kind = 178 | FindWithDefault(*metric_kinds_, metric_value_set.metric_name(), 179 | MetricDescriptor::DELTA); 180 | } 181 | for (const auto& metric_value : metric_value_set.metric_values()) { 182 | string signature = GenerateReportMetricValueSignature(metric_value); 183 | MetricValue* existing = FindOrNull(metric_values, signature); 184 | if (existing == nullptr) { 185 | metric_values.emplace(signature, metric_value); 186 | } else { 187 | MergeMetricValue(metric_kind, metric_value, existing); 188 | } 189 | } 190 | } 191 | } 192 | 193 | } // namespace service_control_client 194 | } // namespace google 195 | -------------------------------------------------------------------------------- /src/operation_aggregator.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // Internal data structure used to cache and aggregate operations. 17 | 18 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_OPERATION_AGGREGATOR_H_ 19 | #define GOOGLE_SERVICE_CONTROL_CLIENT_OPERATION_AGGREGATOR_H_ 20 | 21 | #include 22 | 23 | #include "google/api/metric.pb.h" 24 | #include "google/api/servicecontrol/v1/metric_value.pb.h" 25 | #include "google/api/servicecontrol/v1/operation.pb.h" 26 | #include "utils/google_macros.h" 27 | 28 | namespace google { 29 | namespace service_control_client { 30 | 31 | // Thread compatible. 32 | class OperationAggregator { 33 | public: 34 | // Constructor. Does not take ownership of metric_kinds, which must outlive 35 | // this instance. 36 | OperationAggregator( 37 | const ::google::api::servicecontrol::v1::Operation &operation, 38 | const std::unordered_map 40 | *metric_kinds); 41 | 42 | // Merges the given operation with this operation, assuming the given 43 | // operation has the same operation signature. 44 | void 45 | MergeOperation(const ::google::api::servicecontrol::v1::Operation &operation); 46 | 47 | // Transforms to Operation proto message. 48 | ::google::api::servicecontrol::v1::Operation ToOperationProto() const; 49 | 50 | // Check if the operation is too big. 51 | bool TooBig() const; 52 | 53 | private: 54 | // Merges the metric value sets in the given operation into this operation. 55 | void MergeMetricValueSets( 56 | const ::google::api::servicecontrol::v1::Operation &operation); 57 | 58 | // Merges the log entries in the given operation into this operation. 59 | void MergeLogEntries( 60 | const ::google::api::servicecontrol::v1::Operation &operation); 61 | 62 | // Used to store everything but metric value sets. 63 | ::google::api::servicecontrol::v1::Operation operation_; 64 | 65 | // Aggregated metric values in the operation. 66 | // Key is metric_name. 67 | // Value is a map of metric value signature to aggregated metric value. 68 | std::unordered_map< 69 | std::string, 70 | std::unordered_map> 72 | metric_value_sets_; 73 | 74 | // Metric kinds. Key is the metric name and value is the metric kind. 75 | // Defaults to DELTA if not specified. 76 | const std::unordered_map< 77 | std::string, ::google::api::MetricDescriptor::MetricKind> *metric_kinds_; 78 | 79 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OperationAggregator); 80 | }; 81 | 82 | } // namespace service_control_client 83 | } // namespace google 84 | 85 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_OPERATION_AGGREGATOR_H_ 86 | -------------------------------------------------------------------------------- /src/quota_aggregator_impl.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "src/quota_aggregator_impl.h" 17 | 18 | #include 19 | 20 | 21 | #include "google/protobuf/text_format.h" 22 | #include "src/signature.h" 23 | 24 | using ::google::api::MetricDescriptor; 25 | using ::google::api::servicecontrol::v1::AllocateQuotaRequest; 26 | using ::google::api::servicecontrol::v1::AllocateQuotaResponse; 27 | using ::google::api::servicecontrol::v1::QuotaOperation; 28 | using ::google::api::servicecontrol::v1::QuotaOperation_QuotaMode; 29 | using std::string; 30 | using ::google::service_control_client::SimpleCycleTimer; 31 | 32 | namespace google { 33 | namespace service_control_client { 34 | 35 | void QuotaAggregatorImpl::CacheElem::Aggregate( 36 | const AllocateQuotaRequest& request) { 37 | if (operation_aggregator_ == NULL) { 38 | operation_aggregator_.reset( 39 | new QuotaOperationAggregator(request.allocate_operation())); 40 | } else { 41 | operation_aggregator_->MergeOperation(request.allocate_operation()); 42 | } 43 | } 44 | 45 | AllocateQuotaRequest 46 | QuotaAggregatorImpl::CacheElem::ReturnAllocateQuotaRequestAndClear( 47 | const string& service_name, const std::string& service_config_id) { 48 | AllocateQuotaRequest request; 49 | 50 | request.set_service_name(service_name); 51 | request.set_service_config_id(service_config_id); 52 | 53 | if (operation_aggregator_ != NULL) { 54 | *(request.mutable_allocate_operation()) = 55 | operation_aggregator_->ToOperationProto(); 56 | operation_aggregator_ = NULL; 57 | request.mutable_allocate_operation()->set_quota_mode( 58 | QuotaOperation::BEST_EFFORT); 59 | } else { 60 | // If requests are not aggregated, use the stored initial request 61 | // to allocate minimum token 62 | request = quota_request_; 63 | request.mutable_allocate_operation()->set_quota_mode( 64 | QuotaOperation::CHECK_ONLY); 65 | } 66 | 67 | return request; 68 | } 69 | 70 | QuotaAggregatorImpl::QuotaAggregatorImpl(const std::string& service_name, 71 | const std::string& service_config_id, 72 | const QuotaAggregationOptions& options) 73 | : service_name_(service_name), 74 | service_config_id_(service_config_id), 75 | options_(options), 76 | in_flush_all_(false) { 77 | if (options.num_entries > 0) { 78 | cache_.reset(new QuotaCache( 79 | options.num_entries, std::bind(&QuotaAggregatorImpl::OnCacheEntryDelete, 80 | this, std::placeholders::_1))); 81 | cache_->SetAgeBasedEviction(options.refresh_interval_ms / 1000.0); 82 | } 83 | 84 | refresh_interval_in_cycle_ = 85 | options_.refresh_interval_ms * SimpleCycleTimer::Frequency() / 1000; 86 | 87 | expiration_interval_in_cycle_ = 88 | options_.expiration_interval_ms * SimpleCycleTimer::Frequency() / 1000; 89 | } 90 | 91 | QuotaAggregatorImpl::~QuotaAggregatorImpl() { 92 | SetFlushCallback(NULL); 93 | (void)FlushAll(); 94 | } 95 | 96 | // Sets the flush callback function. 97 | // The callback function must be light and fast. If it needs to make 98 | // a remote call, it must be non-blocking call. 99 | // It should NOT call into this object again from this callback. 100 | // It will cause dead-lock. 101 | void QuotaAggregatorImpl::SetFlushCallback(FlushCallback callback) { 102 | InternalSetFlushCallback(callback); 103 | } 104 | 105 | // If the quota could not be handled by the cache, returns NOT_FOUND, 106 | // caller has to send the request to service control. 107 | // Otherwise, returns OK and cached response. 108 | absl::Status QuotaAggregatorImpl::Quota( 109 | const ::google::api::servicecontrol::v1::AllocateQuotaRequest& request, 110 | ::google::api::servicecontrol::v1::AllocateQuotaResponse* response) { 111 | if (request.service_name() != service_name_) { 112 | return absl::Status( 113 | absl::StatusCode::kInvalidArgument, 114 | (string("Invalid service name: ") + request.service_name() + 115 | string(" Expecting: ") + service_name_)); 116 | } 117 | 118 | if (!request.has_allocate_operation()) { 119 | return absl::Status(absl::StatusCode::kInvalidArgument, 120 | "allocate operation field is required."); 121 | } 122 | 123 | if (!cache_) { 124 | // By returning NO_FOUND, caller will send request to server. 125 | return absl::Status(absl::StatusCode::kNotFound, ""); 126 | } 127 | 128 | AllocateQuotaCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 129 | MutexLock lock(cache_mutex_); 130 | AllocateQuotaCacheRemovedItemsHandler::StackBuffer::Swapper swapper( 131 | this, &stack_buffer); 132 | 133 | string request_signature = GenerateAllocateQuotaRequestSignature(request); 134 | 135 | QuotaCache::ScopedLookup lookup(cache_.get(), request_signature); 136 | if (!lookup.Found()) { 137 | // To avoid sending concurrent allocateQuota from concurrent requests. 138 | // insert a temporary positive response to the cache. Requests from other 139 | // requests will be aggregated to this temporary element until the 140 | // response for the actual request arrives. 141 | ::google::api::servicecontrol::v1::AllocateQuotaResponse temp_response; 142 | CacheElem* cache_elem = 143 | new CacheElem(request, temp_response, SimpleCycleTimer::Now()); 144 | cache_elem->set_signature(request_signature); 145 | cache_elem->set_in_flight(true); 146 | cache_->Insert(request_signature, cache_elem, 1); 147 | 148 | // Call server to report used cost, change mode to BEST_EFFORT. 149 | AllocateQuotaRequest request_copy(request); 150 | request_copy.mutable_allocate_operation()->set_quota_mode( 151 | QuotaOperation::BEST_EFFORT); 152 | AddRemovedItem(request_copy); 153 | 154 | // return positive response 155 | *response = cache_elem->quota_response(); 156 | return absl::OkStatus(); 157 | } 158 | 159 | // Aggregate tokens if the cached response is positive 160 | // In BEST_EFFORT mode, costs should be allocated from the limit. 161 | if (lookup.value()->is_positive_response() || 162 | request.allocate_operation().quota_mode() == 163 | QuotaOperation::BEST_EFFORT) { 164 | lookup.value()->Aggregate(request); 165 | } 166 | 167 | *response = lookup.value()->quota_response(); 168 | return absl::OkStatus(); 169 | } 170 | 171 | // Caches a response from a remote Service Controller AllocateQuota call. 172 | absl::Status QuotaAggregatorImpl::CacheResponse( 173 | const std::string& request_signature, 174 | const ::google::api::servicecontrol::v1::AllocateQuotaResponse& response) { 175 | if (!cache_) { 176 | return absl::OkStatus(); 177 | } 178 | 179 | AllocateQuotaCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 180 | MutexLock lock(cache_mutex_); 181 | AllocateQuotaCacheRemovedItemsHandler::StackBuffer::Swapper swapper( 182 | this, &stack_buffer); 183 | 184 | QuotaCache::ScopedLookup lookup(cache_.get(), request_signature); 185 | if (lookup.Found()) { 186 | lookup.value()->set_in_flight(false); 187 | lookup.value()->set_quota_response(response); 188 | } 189 | 190 | return absl::OkStatus(); 191 | } 192 | 193 | // When the next Flush() should be called. 194 | // Returns in ms from now, or -1 for never 195 | int QuotaAggregatorImpl::GetNextFlushInterval() { 196 | if (!cache_) return -1; 197 | return options_.refresh_interval_ms; 198 | } 199 | 200 | // Check the cached element should be dropped from the cache 201 | bool QuotaAggregatorImpl::ShouldDrop(const CacheElem& elem) const { 202 | int64_t age = SimpleCycleTimer::Now() - elem.last_refresh_time(); 203 | return age >= expiration_interval_in_cycle_; 204 | } 205 | 206 | // Invalidates expired allocate quota responses. 207 | // Called at time specified by GetNextFlushInterval(). 208 | absl::Status QuotaAggregatorImpl::Flush() { 209 | AllocateQuotaCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 210 | MutexLock lock(cache_mutex_); 211 | AllocateQuotaCacheRemovedItemsHandler::StackBuffer::Swapper swapper( 212 | this, &stack_buffer); 213 | 214 | if (cache_) { 215 | cache_->RemoveExpiredEntries(); 216 | } 217 | 218 | return absl::OkStatus(); 219 | } 220 | 221 | // Flushes out all cached check responses; clears all cache items. 222 | // Usually called at destructor. 223 | absl::Status QuotaAggregatorImpl::FlushAll() { 224 | AllocateQuotaCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 225 | MutexLock lock(cache_mutex_); 226 | AllocateQuotaCacheRemovedItemsHandler::StackBuffer::Swapper swapper( 227 | this, &stack_buffer); 228 | 229 | in_flush_all_ = true; 230 | 231 | if (cache_) { 232 | cache_->RemoveAll(); 233 | } 234 | 235 | return absl::OkStatus(); 236 | } 237 | 238 | // OnCacheEntryDelete will be called behind the cache_mutex_ 239 | // no need to consider locking at this point 240 | // 241 | // Each cached item is removed after refresh_interval and 242 | // this OnCacheEntryDelete() is called for each removed item. 243 | // 244 | // This is how it is implemented: 245 | // * cache->SetAgeBasedEviction(refresh_interval) is called at constructor. 246 | // It makes the cache as AgeBased and items older than refresh_interval 247 | // could be evicted. 248 | // * But eviction only happens when the cache is full or 249 | // the function cache->RemoveExpiredEntries() is called. 250 | // * Flush() function calls cache->RemoveExpiredEntries() and it is called 251 | // periodically by service_control_impl.cc at refresh_interval. 252 | // 253 | void QuotaAggregatorImpl::OnCacheEntryDelete(CacheElem* elem) { 254 | if (in_flush_all_ || ShouldDrop(*elem)) { 255 | delete elem; 256 | return; 257 | } 258 | 259 | if (elem->in_flight()) { 260 | // This item is still calling the server, add it back to the cache 261 | // to wait for the response. 262 | cache_->Insert(elem->signature(), elem, 1); 263 | return; 264 | } 265 | 266 | // For an aggregated item, send the aggregated cost to the server. 267 | // For an negative item, send CHECK_ONLY to check available quota. 268 | if (elem->is_aggregated() || !elem->is_positive_response()) { 269 | elem->set_in_flight(true); 270 | elem->set_last_refresh_time(SimpleCycleTimer::Now()); 271 | auto request = elem->ReturnAllocateQuotaRequestAndClear(service_name_, 272 | service_config_id_); 273 | // Insert the element back to the cache 274 | // This is important for negative items to reject new requests. 275 | cache_->Insert(elem->signature(), elem, 1); 276 | // AddRemovedItem function name is misleading, it actually calls 277 | // transport function to send the request to server. 278 | AddRemovedItem(request); 279 | return; 280 | } 281 | 282 | // The postive and non-aggregated items reach here. Add them back to 283 | // the cache to reduce quota allocation calls. Even through removing them will 284 | // reduce cache size, but it will increase cache misses and quota calls since 285 | // each cache miss will cause a quota call. 286 | cache_->Insert(elem->signature(), elem, 1); 287 | } 288 | 289 | std::unique_ptr CreateAllocateQuotaAggregator( 290 | const std::string& service_name, const std::string& service_config_id, 291 | const QuotaAggregationOptions& options) { 292 | return std::unique_ptr( 293 | new QuotaAggregatorImpl(service_name, service_config_id, options)); 294 | } 295 | 296 | } // namespace service_control_client 297 | } // namespace google 298 | -------------------------------------------------------------------------------- /src/quota_aggregator_impl.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_QUOTA_AGGREGATOR_IMPL_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_QUOTA_AGGREGATOR_IMPL_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "google/api/metric.pb.h" 24 | #include "google/api/servicecontrol/v1/operation.pb.h" 25 | #include "google/api/servicecontrol/v1/quota_controller.pb.h" 26 | #include "google/api/servicecontrol/v1/service_controller.pb.h" 27 | #include "google/protobuf/text_format.h" 28 | #include "src/aggregator_interface.h" 29 | #include "src/cache_removed_items_handler.h" 30 | #include "src/quota_operation_aggregator.h" 31 | #include "utils/simple_lru_cache.h" 32 | #include "utils/simple_lru_cache_inl.h" 33 | #include "utils/thread.h" 34 | 35 | namespace google { 36 | namespace service_control_client { 37 | 38 | typedef CacheRemovedItemsHandler< 39 | ::google::api::servicecontrol::v1::AllocateQuotaRequest> 40 | AllocateQuotaCacheRemovedItemsHandler; 41 | 42 | // 43 | // The algorithm is described in this doc: go/esp-quota-cache 44 | // 45 | class QuotaAggregatorImpl : public QuotaAggregator, 46 | public AllocateQuotaCacheRemovedItemsHandler { 47 | public: 48 | // TODO(jaebong) when an element was expired and aggregated tokens are bigger 49 | // than 0, aggregator needs to send a request inside the aggregator. 50 | QuotaAggregatorImpl(const std::string &service_name, 51 | const std::string &service_config_id, 52 | const QuotaAggregationOptions &options); 53 | 54 | virtual ~QuotaAggregatorImpl(); 55 | 56 | // Sets the flush callback function. 57 | // The callback function must be light and fast. If it needs to make 58 | // a remote call, it must be non-blocking call. 59 | // It should NOT call into this object again from this callback. 60 | // It will cause dead-lock. 61 | void SetFlushCallback(FlushCallback callback); 62 | 63 | // If the quota could not be handled by the cache, returns NOT_FOUND, 64 | // caller has to send the request to service control. 65 | // Otherwise, returns OK and cached response. 66 | absl::Status 67 | Quota(const ::google::api::servicecontrol::v1::AllocateQuotaRequest &request, 68 | ::google::api::servicecontrol::v1::AllocateQuotaResponse *response); 69 | 70 | // Caches a response from a remote Service Controller AllocateQuota call. 71 | absl::Status CacheResponse( 72 | const std::string &request_signature, 73 | const ::google::api::servicecontrol::v1::AllocateQuotaResponse &response); 74 | 75 | private: 76 | class CacheElem { 77 | public: 78 | CacheElem( 79 | const ::google::api::servicecontrol::v1::AllocateQuotaRequest &request, 80 | const ::google::api::servicecontrol::v1::AllocateQuotaResponse 81 | &response, 82 | const int64_t time) 83 | : operation_aggregator_(nullptr), quota_request_(request), 84 | quota_response_(response), last_refresh_time_(time), 85 | in_flight_(false) {} 86 | 87 | // Aggregates the given request to this cache entry. 88 | void Aggregate( 89 | const ::google::api::servicecontrol::v1::AllocateQuotaRequest &request); 90 | 91 | // Returns the aggregated AllocateQuotaRequest and reset the cache entry. 92 | ::google::api::servicecontrol::v1::AllocateQuotaRequest 93 | ReturnAllocateQuotaRequestAndClear(const std::string &service_name, 94 | const std::string &service_config_id); 95 | 96 | // Change the negative response to the positive response for refreshing 97 | void ClearAllocationErrors() { quota_response_.clear_allocate_errors(); } 98 | 99 | // Setter for quota_response_. 100 | inline void set_quota_response( 101 | const ::google::api::servicecontrol::v1::AllocateQuotaResponse 102 | "a_response) { 103 | quota_response_ = quota_response; 104 | 105 | if (quota_response.allocate_errors_size() > 0) { 106 | operation_aggregator_ = NULL; 107 | } 108 | } 109 | 110 | // Getter for quota_response_. 111 | inline const ::google::api::servicecontrol::v1::AllocateQuotaResponse & 112 | quota_response() const { 113 | return quota_response_; 114 | } 115 | 116 | // Return true if aggregated 117 | inline bool is_aggregated() const { 118 | return operation_aggregator_ != nullptr; 119 | } 120 | 121 | // Getter and Setter of signature_ 122 | inline std::string signature() const { return signature_; } 123 | inline void set_signature(std::string v) { signature_ = v; } 124 | 125 | // Getter and Setter of in_flight_ 126 | inline bool in_flight() const { return in_flight_; } 127 | inline void set_in_flight(bool v) { in_flight_ = v; } 128 | 129 | inline bool is_positive_response() { 130 | return quota_response().allocate_errors_size() == 0; 131 | } 132 | 133 | // Setter for last check time. 134 | inline void set_last_refresh_time(const int64_t last_refresh_time) { 135 | last_refresh_time_ = last_refresh_time; 136 | } 137 | // Getter for last check time. 138 | inline const int64_t last_refresh_time() const { 139 | return last_refresh_time_; 140 | } 141 | 142 | private: 143 | // Internal operation. 144 | std::unique_ptr operation_aggregator_; 145 | 146 | // The AllocateQuotaRequest for the initial allocate quota request. 147 | ::google::api::servicecontrol::v1::AllocateQuotaRequest quota_request_; 148 | 149 | // The AllocateQuotaResponse for the last request. 150 | ::google::api::servicecontrol::v1::AllocateQuotaResponse quota_response_; 151 | 152 | // maintain the signature to move unnecessary signature generation 153 | std::string signature_; 154 | 155 | // the last refresh time of the cached element 156 | int64_t last_refresh_time_; 157 | 158 | // the element is waiting for the response 159 | bool in_flight_; 160 | }; 161 | 162 | using CacheDeleter = std::function; 163 | 164 | // Key is the signature of the check request. Value is the CacheElem. 165 | // It is a LRU cache with MaxIdelTime as response_expiration_time. 166 | using QuotaCache = 167 | SimpleLRUCacheWithDeleter; 168 | 169 | // Methods from: QuotaAggregator interface 170 | 171 | void OnCacheEntryDelete(CacheElem *elem); 172 | 173 | // When the next Flush() should be called. 174 | // Returns in ms from now, or -1 for never 175 | virtual int GetNextFlushInterval(); 176 | 177 | // Invalidates expired allocate quota responses. 178 | // Called at time specified by GetNextFlushInterval(). 179 | virtual absl::Status Flush(); 180 | 181 | // Flushes out all cached check responses; clears all cache items. 182 | // Usually called at destructor. 183 | virtual absl::Status FlushAll(); 184 | 185 | bool ShouldDrop(const CacheElem &elem) const; 186 | 187 | private: 188 | // The service name for this cache. 189 | const std::string service_name_; 190 | // The service config id for this cache. 191 | const std::string service_config_id_; 192 | 193 | // The check aggregation options. 194 | QuotaAggregationOptions options_; 195 | 196 | // Mutex guarding the access of cache_; 197 | Mutex cache_mutex_; 198 | 199 | std::unique_ptr cache_; 200 | 201 | // flush interval in cycles. 202 | int64_t refresh_interval_in_cycle_; 203 | 204 | // expire interval in cycle 205 | int64_t expiration_interval_in_cycle_; 206 | 207 | bool in_flush_all_; 208 | 209 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(QuotaAggregatorImpl); 210 | }; 211 | 212 | } // namespace service_control_client 213 | } // namespace google 214 | 215 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_QUOTA_AGGREGATOR_IMPL_H_ 216 | -------------------------------------------------------------------------------- /src/quota_operation_aggregator.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "src/quota_operation_aggregator.h" 17 | 18 | #include 19 | 20 | #include "absl/log/absl_check.h" 21 | #include "absl/log/absl_log.h" 22 | #include "src/signature.h" 23 | #include "utils/distribution_helper.h" 24 | #include "utils/stl_util.h" 25 | 26 | using google::api::MetricDescriptor; 27 | using google::api::servicecontrol::v1::MetricValue; 28 | using google::api::servicecontrol::v1::MetricValueSet; 29 | using google::api::servicecontrol::v1::QuotaOperation; 30 | using ::google::protobuf::Timestamp; 31 | using std::string; 32 | 33 | namespace google { 34 | namespace service_control_client { 35 | 36 | namespace { 37 | 38 | // Returns whether timestamp a is before b or not. 39 | bool TimestampBefore(const Timestamp& a, const Timestamp& b) { 40 | return a.seconds() < b.seconds() || 41 | (a.seconds() == b.seconds() && a.nanos() < b.nanos()); 42 | } 43 | 44 | // Merges two metric values, with metric kind being Delta. 45 | // 46 | // For INT64, values will be added together, 47 | // except no change when the bucket options does not match. 48 | void MergeDeltaMetricValue(const MetricValue& from, MetricValue* to) { 49 | if (to->value_case() != from.value_case()) { 50 | ABSL_LOG(WARNING) << "Metric values are not compatible: " 51 | << from.DebugString() << ", " << to->DebugString(); 52 | return; 53 | } 54 | 55 | if (from.has_start_time()) { 56 | if (!to->has_start_time() || 57 | TimestampBefore(from.start_time(), to->start_time())) { 58 | *(to->mutable_start_time()) = from.start_time(); 59 | } 60 | } 61 | 62 | if (from.has_end_time()) { 63 | if (!to->has_end_time() || 64 | TimestampBefore(to->end_time(), from.end_time())) { 65 | *(to->mutable_end_time()) = from.end_time(); 66 | } 67 | } 68 | 69 | switch (to->value_case()) { 70 | case MetricValue::kInt64Value: 71 | to->set_int64_value(to->int64_value() + from.int64_value()); 72 | break; 73 | default: 74 | ABSL_LOG(WARNING) << "Unknown metric kind for: " << to->DebugString(); 75 | break; 76 | } 77 | } 78 | 79 | } // namespace 80 | 81 | QuotaOperationAggregator::QuotaOperationAggregator( 82 | const ::google::api::servicecontrol::v1::QuotaOperation& operation) 83 | : operation_(operation) { 84 | MergeOperation(operation); 85 | } 86 | 87 | void QuotaOperationAggregator::MergeOperation(const QuotaOperation& operation) { 88 | for (const auto& metric_value_set : operation.quota_metrics()) { 89 | ABSL_DCHECK(metric_value_set.metric_values_size() == 1); 90 | 91 | auto found = metric_value_sets_.find(metric_value_set.metric_name()); 92 | if (found == metric_value_sets_.end()) { 93 | metric_value_sets_[metric_value_set.metric_name()] = 94 | metric_value_set.metric_values(0); 95 | } else { 96 | MergeDeltaMetricValue(metric_value_set.metric_values(0), &found->second); 97 | } 98 | } 99 | } 100 | 101 | QuotaOperation QuotaOperationAggregator::ToOperationProto() const { 102 | QuotaOperation op(operation_); 103 | op.clear_quota_metrics(); 104 | 105 | for (auto metric_values : metric_value_sets_) { 106 | MetricValueSet* set = op.add_quota_metrics(); 107 | set->set_metric_name(metric_values.first); 108 | 109 | *(set->add_metric_values()) = metric_values.second; 110 | } 111 | 112 | return op; 113 | } 114 | 115 | } // namespace service_control_client 116 | } // namespace google 117 | -------------------------------------------------------------------------------- /src/quota_operation_aggregator.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_QUOTA_OPERATION_AGGREGATOR_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_QUOTA_OPERATION_AGGREGATOR_H_ 18 | 19 | #include 20 | 21 | #include "absl/status/status.h" 22 | #include "google/api/metric.pb.h" 23 | #include "google/api/servicecontrol/v1/quota_controller.pb.h" 24 | #include "google/protobuf/text_format.h" 25 | #include "utils/google_macros.h" 26 | 27 | namespace google { 28 | namespace service_control_client { 29 | 30 | class QuotaOperationAggregator { 31 | public: 32 | QuotaOperationAggregator( 33 | const ::google::api::servicecontrol::v1::QuotaOperation &operation); 34 | virtual ~QuotaOperationAggregator(){}; 35 | 36 | // QuotaOperationsAggregator is neither copyable nor movable. 37 | QuotaOperationAggregator(const QuotaOperationAggregator &) = delete; 38 | QuotaOperationAggregator & 39 | operator=(const QuotaOperationAggregator &) = delete; 40 | 41 | public: 42 | // Merges the given operation with this operation, assuming the given 43 | // operation has the same operation signature. 44 | void MergeOperation( 45 | const ::google::api::servicecontrol::v1::QuotaOperation &operation); 46 | 47 | // Transforms to Operation proto message. 48 | ::google::api::servicecontrol::v1::QuotaOperation ToOperationProto() const; 49 | 50 | private: 51 | // Merges the metric value sets in the given operation into this operation. 52 | bool MergeMetricValueSets( 53 | const ::google::api::servicecontrol::v1::QuotaOperation &operation); 54 | 55 | // Used to store everything but metric value sets. 56 | ::google::api::servicecontrol::v1::QuotaOperation operation_; 57 | 58 | // Aggregated metric values in the operation. 59 | // Key is metric_name. 60 | // Value is a map of metric value signature to aggregated metric value. 61 | std::unordered_map 63 | metric_value_sets_; 64 | }; 65 | 66 | } // namespace service_control_client 67 | } // namespace google 68 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_QUOTA_OPERATION_AGGREGATOR_H_ 69 | -------------------------------------------------------------------------------- /src/quota_operation_aggregator_test.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "src/quota_operation_aggregator.h" 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include "gmock/gmock.h" 23 | #include "google/protobuf/text_format.h" 24 | #include "google/protobuf/util/message_differencer.h" 25 | #include "gtest/gtest.h" 26 | #include "utils/status_test_util.h" 27 | 28 | using ::google::api::servicecontrol::v1::QuotaOperation; 29 | using ::google::protobuf::TextFormat; 30 | using ::google::protobuf::util::MessageDifferencer; 31 | using std::string; 32 | 33 | namespace google { 34 | namespace service_control_client { 35 | namespace { 36 | 37 | const char kOperation1[] = R"( 38 | operation_id: "operationid" 39 | method_name: "methodname" 40 | consumer_id: "consumerid" 41 | quota_metrics { 42 | metric_name: "metric_first" 43 | metric_values { 44 | int64_value: 1 45 | } 46 | } 47 | quota_metrics { 48 | metric_name: "metric_second" 49 | metric_values { 50 | int64_value: 1 51 | } 52 | } 53 | quota_mode: BEST_EFFORT 54 | )"; 55 | 56 | const char kOperation2[] = R"( 57 | operation_id: "operationid" 58 | method_name: "methodname" 59 | consumer_id: "consumerid" 60 | quota_metrics { 61 | metric_name: "metric_first" 62 | metric_values { 63 | int64_value: 2 64 | } 65 | } 66 | quota_metrics { 67 | metric_name: "metric_second" 68 | metric_values { 69 | int64_value: 3 70 | } 71 | } 72 | quota_mode: BEST_EFFORT 73 | )"; 74 | 75 | const std::set> ExtractMetricSets( 76 | const ::google::api::servicecontrol::v1::QuotaOperation& operation) { 77 | std::set> sets; 78 | 79 | for (auto quota_metric : operation.quota_metrics()) { 80 | sets.insert(std::make_pair(quota_metric.metric_name(), 81 | quota_metric.metric_values(0).int64_value())); 82 | } 83 | 84 | return sets; 85 | } 86 | 87 | } // namespace 88 | 89 | class QuotaOperationAggregatorImplTest : public ::testing::Test { 90 | public: 91 | void SetUp() { 92 | ASSERT_TRUE(TextFormat::ParseFromString(kOperation1, &operation1_)); 93 | ASSERT_TRUE(TextFormat::ParseFromString(kOperation2, &operation2_)); 94 | } 95 | 96 | ::google::api::servicecontrol::v1::QuotaOperation operation1_; 97 | ::google::api::servicecontrol::v1::QuotaOperation operation2_; 98 | }; 99 | 100 | TEST_F(QuotaOperationAggregatorImplTest, TestInitialization) { 101 | QuotaOperationAggregator aggregator(operation1_); 102 | 103 | QuotaOperation operation = aggregator.ToOperationProto(); 104 | 105 | std::set> quota_metrics = 106 | ExtractMetricSets(operation); 107 | std::set> expected_costs = {{"metric_first", 1}, 108 | {"metric_second", 1}}; 109 | ASSERT_EQ(quota_metrics, expected_costs); 110 | } 111 | 112 | TEST_F(QuotaOperationAggregatorImplTest, TestMergeOperation) { 113 | QuotaOperationAggregator aggregator(operation1_); 114 | 115 | aggregator.MergeOperation(operation2_); 116 | 117 | QuotaOperation operation = aggregator.ToOperationProto(); 118 | std::set> quota_metrics = 119 | ExtractMetricSets(operation); 120 | std::set> expected_costs = {{"metric_first", 3}, 121 | {"metric_second", 4}}; 122 | ASSERT_EQ(quota_metrics, expected_costs); 123 | } 124 | 125 | } // namespace service_control_client 126 | } // namespace google 127 | -------------------------------------------------------------------------------- /src/report_aggregator_impl.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "src/report_aggregator_impl.h" 17 | 18 | 19 | #include "src/signature.h" 20 | 21 | using ::google::api::MetricDescriptor; 22 | using ::google::api::servicecontrol::v1::Operation; 23 | using ::google::api::servicecontrol::v1::ReportRequest; 24 | using ::google::api::servicecontrol::v1::ReportResponse; 25 | using std::string; 26 | 27 | namespace google { 28 | namespace service_control_client { 29 | namespace { 30 | 31 | // Service control server limits each report data size to 1MB. 32 | // Roughly, each operation may have up to 50KB based on maximum of 100 33 | // aggregated logEntries (each log entry is about 500 bytes). 34 | const int kMaxOperationsToSend = 10; 35 | 36 | // Returns whether the given report request has high value operations. 37 | bool HasHighImportantOperation(const ReportRequest& request) { 38 | for (const auto& operation : request.operations()) { 39 | if (operation.importance() != Operation::LOW) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | } 45 | 46 | } // namespace 47 | 48 | ReportAggregatorImpl::ReportAggregatorImpl( 49 | const string& service_name, const std::string& service_config_id, 50 | const ReportAggregationOptions& options, 51 | std::shared_ptr metric_kinds) 52 | : service_name_(service_name), 53 | service_config_id_(service_config_id), 54 | options_(options), 55 | metric_kinds_(metric_kinds) { 56 | if (options.num_entries > 0) { 57 | cache_.reset( 58 | new ReportCache(options.num_entries, 59 | std::bind(&ReportAggregatorImpl::OnCacheEntryDelete, 60 | this, std::placeholders::_1))); 61 | cache_->SetAgeBasedEviction(options.flush_interval_ms / 1000.0); 62 | } 63 | } 64 | 65 | ReportAggregatorImpl::~ReportAggregatorImpl() { 66 | // FlushAll() is a blocking call to remove all cache items. 67 | // For each removed item, it will call flush_callback(). 68 | // At the destructor, it is better not to call the callback. 69 | SetFlushCallback(NULL); 70 | (void)FlushAll(); 71 | } 72 | 73 | // Set the flush callback function. 74 | void ReportAggregatorImpl::SetFlushCallback(FlushCallback callback) { 75 | InternalSetFlushCallback(callback); 76 | } 77 | 78 | // Add a report request to cache 79 | absl::Status ReportAggregatorImpl::Report( 80 | const ::google::api::servicecontrol::v1::ReportRequest& request) { 81 | if (request.service_name() != service_name_) { 82 | return absl::Status( 83 | absl::StatusCode::kInvalidArgument, 84 | (string("Invalid service name: ") + request.service_name() + 85 | string(" Expecting: ") + service_name_)); 86 | } 87 | if (HasHighImportantOperation(request) || !cache_) { 88 | // By returning NO_FOUND, caller will send request to server. 89 | return absl::Status(absl::StatusCode::kNotFound, ""); 90 | } 91 | 92 | ReportCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 93 | MutexLock lock(cache_mutex_); 94 | ReportCacheRemovedItemsHandler::StackBuffer::Swapper swapper(this, 95 | &stack_buffer); 96 | 97 | // Starts to cache and aggregate low important operations. 98 | for (const auto& operation : request.operations()) { 99 | string signature = GenerateReportOperationSignature(operation); 100 | 101 | bool too_big = false; 102 | { 103 | ReportCache::ScopedLookup lookup(cache_.get(), signature); 104 | if (lookup.Found()) { 105 | lookup.value()->MergeOperation(operation); 106 | too_big = lookup.value()->TooBig(); 107 | } else { 108 | OperationAggregator* iop = 109 | new OperationAggregator(operation, metric_kinds_.get()); 110 | cache_->Insert(signature, iop, 1); 111 | } 112 | } 113 | // If the merged operation is too big, remove it from the cache 114 | // to flush it out. Make sure to do that outside of lookup scope. 115 | if (too_big) { 116 | cache_->Remove(signature); 117 | } 118 | } 119 | return absl::OkStatus(); 120 | } 121 | 122 | void ReportAggregatorImpl::OnCacheEntryDelete(OperationAggregator* iop) { 123 | // iop or cache is under projected. This function is only called when 124 | // cache::Insert() or cache::Removed() is called and these operations 125 | // are already protected by cache_mutex. 126 | ReportRequest request; 127 | request.set_service_name(service_name_); 128 | request.set_service_config_id(service_config_id_); 129 | // TODO(qiwzhang): Remove this copy 130 | *(request.add_operations()) = iop->ToOperationProto(); 131 | delete iop; 132 | 133 | AddRemovedItem(request); 134 | } 135 | 136 | bool ReportAggregatorImpl::MergeItem(const ReportRequest& new_item, 137 | ReportRequest* old_item) { 138 | if (old_item->service_name() != new_item.service_name() || 139 | old_item->operations().size() + new_item.operations().size() > 140 | kMaxOperationsToSend) { 141 | return false; 142 | } 143 | old_item->MergeFrom(new_item); 144 | return true; 145 | } 146 | 147 | // When the next Flush() should be called. 148 | // Return in ms from now, or -1 for never 149 | int ReportAggregatorImpl::GetNextFlushInterval() { 150 | if (!cache_) return -1; 151 | return options_.flush_interval_ms; 152 | } 153 | 154 | // Flush aggregated requests whom are longer than flush_interval. 155 | // Called at time specified by GetNextFlushInterval(). 156 | absl::Status ReportAggregatorImpl::Flush() { 157 | ReportCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 158 | MutexLock lock(cache_mutex_); 159 | ReportCacheRemovedItemsHandler::StackBuffer::Swapper swapper(this, 160 | &stack_buffer); 161 | if (cache_) { 162 | cache_->RemoveExpiredEntries(); 163 | } 164 | return absl::OkStatus(); 165 | } 166 | 167 | // Flush out aggregated report requests, clear all cache items. 168 | // Usually called at destructor. 169 | absl::Status ReportAggregatorImpl::FlushAll() { 170 | ReportCacheRemovedItemsHandler::StackBuffer stack_buffer(this); 171 | MutexLock lock(cache_mutex_); 172 | ReportCacheRemovedItemsHandler::StackBuffer::Swapper swapper(this, 173 | &stack_buffer); 174 | if (cache_) { 175 | cache_->RemoveAll(); 176 | } 177 | return absl::OkStatus(); 178 | } 179 | 180 | std::unique_ptr CreateReportAggregator( 181 | const std::string& service_name, const std::string& service_config_id, 182 | const ReportAggregationOptions& options, 183 | std::shared_ptr metric_kind) { 184 | return std::unique_ptr(new ReportAggregatorImpl( 185 | service_name, service_config_id, options, metric_kind)); 186 | } 187 | 188 | } // namespace service_control_client 189 | } // namespace google 190 | -------------------------------------------------------------------------------- /src/report_aggregator_impl.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // Caches and aggregates report requests. 17 | 18 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_REPORT_AGGREGATOR_IMPL_H_ 19 | #define GOOGLE_SERVICE_CONTROL_CLIENT_REPORT_AGGREGATOR_IMPL_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "absl/status/status.h" 26 | #include "google/api/metric.pb.h" 27 | #include "google/api/servicecontrol/v1/operation.pb.h" 28 | #include "google/api/servicecontrol/v1/service_controller.pb.h" 29 | #include "src/aggregator_interface.h" 30 | #include "src/cache_removed_items_handler.h" 31 | #include "src/operation_aggregator.h" 32 | #include "utils/simple_lru_cache.h" 33 | #include "utils/simple_lru_cache_inl.h" 34 | #include "utils/thread.h" 35 | 36 | namespace google { 37 | namespace service_control_client { 38 | 39 | // Caches/Batches/aggregates report requests and sends them to the server. 40 | // Thread safe. 41 | typedef CacheRemovedItemsHandler< 42 | ::google::api::servicecontrol::v1::ReportRequest> 43 | ReportCacheRemovedItemsHandler; 44 | 45 | class ReportAggregatorImpl : public ReportAggregator, 46 | public ReportCacheRemovedItemsHandler { 47 | public: 48 | // Constructor. 49 | ReportAggregatorImpl(const std::string &service_name, 50 | const std::string &service_config_id, 51 | const ReportAggregationOptions &options, 52 | std::shared_ptr metric_kind); 53 | 54 | virtual ~ReportAggregatorImpl(); 55 | 56 | // Sets the flush callback function. 57 | virtual void SetFlushCallback(FlushCallback callback); 58 | 59 | // Adds a report request to cache. Returns NOT_FOUND if it could not be 60 | // aggregated. Callers need to send it to the server. 61 | virtual absl::Status 62 | Report(const ::google::api::servicecontrol::v1::ReportRequest &request); 63 | 64 | // When the next Flush() should be called. 65 | // Returns in ms from now, or -1 for never 66 | virtual int GetNextFlushInterval(); 67 | 68 | // Flushes aggregated requests longer than flush_interval. 69 | // Called at time specified by GetNextFlushInterval(). 70 | virtual absl::Status Flush(); 71 | 72 | // Flushes all cache items. For each item, it will call flush_callback. 73 | // It is a blocking call, only returns when all items are removed. 74 | // When calling flush_callback, it is a blocking call too, it will wait for 75 | // the flush_callback() function return. 76 | virtual absl::Status FlushAll(); 77 | 78 | private: 79 | using CacheDeleter = std::function; 80 | // Key is the signature of the operation. Value is the 81 | // OperationAggregator. 82 | using ReportCache = 83 | SimpleLRUCacheWithDeleter; 84 | 85 | // Callback function passed to Cache, called when a cache item is removed. 86 | // Takes ownership of the iop. 87 | void OnCacheEntryDelete(OperationAggregator *iop); 88 | 89 | // Tries to merge two report requests. 90 | bool 91 | MergeItem(const ::google::api::servicecontrol::v1::ReportRequest &new_item, 92 | ::google::api::servicecontrol::v1::ReportRequest *old_item); 93 | 94 | // The service name. 95 | const std::string service_name_; 96 | // The service config id. 97 | const std::string service_config_id_; 98 | 99 | ReportAggregationOptions options_; 100 | 101 | // Metric kinds. Key is the metric name and value is the metric kind. 102 | // Defaults to DELTA if not specified. Not owned. 103 | std::shared_ptr metric_kinds_; 104 | 105 | // Mutex guarding the access of cache_; 106 | Mutex cache_mutex_; 107 | 108 | // The cache that maps from operation signature to an operation. 109 | // We don't calculate fine grained cost for cache entries, assign each 110 | // entry 1 cost unit. 111 | // Guarded by mutex_, except when compare against nullptr. 112 | std::unique_ptr cache_; 113 | 114 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ReportAggregatorImpl); 115 | }; 116 | 117 | } // namespace service_control_client 118 | } // namespace google 119 | 120 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_REPORT_AGGREGATOR_IMPL_H_ 121 | -------------------------------------------------------------------------------- /src/report_aggregator_impl_test.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "src/report_aggregator_impl.h" 17 | 18 | #include 19 | 20 | #include "gmock/gmock.h" 21 | 22 | #include "google/protobuf/text_format.h" 23 | #include "google/protobuf/util/message_differencer.h" 24 | #include "gtest/gtest.h" 25 | #include "utils/status_test_util.h" 26 | 27 | using ::google::api::MetricDescriptor; 28 | using ::google::api::servicecontrol::v1::Operation; 29 | using ::google::api::servicecontrol::v1::ReportRequest; 30 | using ::google::api::servicecontrol::v1::ReportResponse; 31 | using ::google::protobuf::TextFormat; 32 | using ::google::protobuf::util::MessageDifferencer; 33 | using std::string; 34 | 35 | namespace google { 36 | namespace service_control_client { 37 | namespace { 38 | 39 | const char kServiceName[] = "library.googleapis.com"; 40 | const char kServiceConfigId[] = "2016-09-19r0"; 41 | 42 | const char kRequest1[] = R"( 43 | service_name: "library.googleapis.com" 44 | service_config_id: "2016-09-19r0" 45 | operations: { 46 | operation_id: "operation-1" 47 | consumer_id: "project:some-consumer" 48 | start_time { 49 | seconds: 1000 50 | nanos: 2000 51 | } 52 | end_time { 53 | seconds: 3000 54 | nanos: 4000 55 | } 56 | log_entries { 57 | timestamp { 58 | seconds: 700 59 | nanos: 600 60 | } 61 | severity: INFO 62 | name: "system_event" 63 | text_payload: "Sample text log message 0" 64 | } 65 | metric_value_sets { 66 | metric_name: "library.googleapis.com/rpc/client/count" 67 | metric_values { 68 | start_time { 69 | seconds: 100 70 | } 71 | end_time { 72 | seconds: 300 73 | } 74 | int64_value: 1000 75 | } 76 | } 77 | } 78 | )"; 79 | 80 | const char kRequest2[] = R"( 81 | service_name: "library.googleapis.com" 82 | service_config_id: "2016-09-19r0" 83 | operations: { 84 | operation_id: "operation-2" 85 | consumer_id: "project:some-consumer" 86 | start_time { 87 | seconds: 1000 88 | nanos: 2000 89 | } 90 | end_time { 91 | seconds: 3000 92 | nanos: 4000 93 | } 94 | log_entries { 95 | timestamp { 96 | seconds: 700 97 | nanos: 600 98 | } 99 | severity: INFO 100 | name: "system_event" 101 | text_payload: "Sample text log message 1" 102 | } 103 | metric_value_sets { 104 | metric_name: "library.googleapis.com/rpc/client/count" 105 | metric_values { 106 | start_time { 107 | seconds: 200 108 | } 109 | end_time { 110 | seconds: 400 111 | } 112 | int64_value: 2000 113 | } 114 | } 115 | } 116 | )"; 117 | 118 | // Result of Merging request 1 into request 2, assuming they have delta metrics. 119 | const char kDeltaMerged12[] = R"( 120 | service_name: "library.googleapis.com" 121 | service_config_id: "2016-09-19r0" 122 | operations: { 123 | operation_id: "operation-1" 124 | consumer_id: "project:some-consumer" 125 | start_time { 126 | seconds: 1000 127 | nanos: 2000 128 | } 129 | end_time { 130 | seconds: 3000 131 | nanos: 4000 132 | } 133 | metric_value_sets { 134 | metric_name: "library.googleapis.com/rpc/client/count" 135 | metric_values { 136 | start_time { 137 | seconds: 100 138 | } 139 | end_time { 140 | seconds: 400 141 | } 142 | int64_value: 3000 143 | } 144 | } 145 | log_entries { 146 | severity: INFO 147 | timestamp { 148 | seconds: 700 149 | nanos: 600 150 | } 151 | text_payload: "Sample text log message 0" 152 | name: "system_event" 153 | } 154 | log_entries { 155 | severity: INFO 156 | timestamp { 157 | seconds: 700 158 | nanos: 600 159 | } 160 | text_payload: "Sample text log message 1" 161 | name: "system_event" 162 | } 163 | } 164 | )"; 165 | } // namespace 166 | 167 | class ReportAggregatorImplTest : public ::testing::Test { 168 | public: 169 | void SetUp() { 170 | ASSERT_TRUE(TextFormat::ParseFromString(kRequest1, &request1_)); 171 | ASSERT_TRUE(TextFormat::ParseFromString(kRequest2, &request2_)); 172 | ASSERT_TRUE(TextFormat::ParseFromString(kDeltaMerged12, &delta_merged12_)); 173 | 174 | ReportAggregationOptions options(1 /*entries*/, 1000 /*flush_interval_ms*/); 175 | aggregator_ = CreateReportAggregator( 176 | kServiceName, kServiceConfigId, options, 177 | std::shared_ptr(new MetricKindMap)); 178 | ASSERT_TRUE((bool)(aggregator_)); 179 | aggregator_->SetFlushCallback(std::bind( 180 | &ReportAggregatorImplTest::FlushCallback, this, std::placeholders::_1)); 181 | } 182 | 183 | // Adds a label to the given operation. 184 | void AddLabel(const string& key, const string& value, Operation* operation) { 185 | (*operation->mutable_labels())[key] = value; 186 | } 187 | 188 | void FlushCallback(const ReportRequest& request) { 189 | flushed_.push_back(request); 190 | } 191 | 192 | void FlushCallbackCallingBackToAggregator(const ReportRequest& request) { 193 | flushed_.push_back(request); 194 | (void)aggregator_->Flush(); 195 | } 196 | 197 | ReportRequest request1_; 198 | ReportRequest request2_; 199 | ReportRequest delta_merged12_; 200 | 201 | ReportResponse response_; 202 | 203 | std::unique_ptr aggregator_; 204 | std::vector flushed_; 205 | }; 206 | 207 | TEST_F(ReportAggregatorImplTest, TestNotMatchingServiceName) { 208 | *(request1_.mutable_service_name()) = "some-other-service-name"; 209 | EXPECT_EQ(absl::StatusCode::kInvalidArgument, 210 | aggregator_->Report(request1_).code()); 211 | // Nothing flush out 212 | EXPECT_EQ(flushed_.size(), 0); 213 | } 214 | 215 | TEST_F(ReportAggregatorImplTest, TestNoOperation) { 216 | request1_.clear_operations(); 217 | EXPECT_OK(aggregator_->Report(request1_)); 218 | // Nothing flush out 219 | EXPECT_EQ(flushed_.size(), 0); 220 | } 221 | 222 | TEST_F(ReportAggregatorImplTest, TestAddOperation1) { 223 | EXPECT_OK(aggregator_->Report(request1_)); 224 | // Item cached, not flushed out 225 | EXPECT_EQ(flushed_.size(), 0); 226 | 227 | EXPECT_OK(aggregator_->FlushAll()); 228 | EXPECT_EQ(flushed_.size(), 1); 229 | EXPECT_TRUE(MessageDifferencer::Equals(flushed_[0], request1_)); 230 | } 231 | 232 | TEST_F(ReportAggregatorImplTest, TestFlushOutMaxLogEntry) { 233 | // Each operation can append up to 100 log entries. 234 | for (int i = 0; i < 100; ++i) { 235 | EXPECT_OK(aggregator_->Report(request1_)); 236 | } 237 | // With 100 log entries, the item should be flushed out. 238 | EXPECT_EQ(flushed_.size(), 1); 239 | EXPECT_EQ(flushed_[0].operations(0).log_entries_size(), 100); 240 | } 241 | 242 | TEST_F(ReportAggregatorImplTest, TestAddOperation12) { 243 | EXPECT_OK(aggregator_->Report(request1_)); 244 | // Item cached, not flushed out 245 | EXPECT_EQ(flushed_.size(), 0); 246 | 247 | EXPECT_OK(aggregator_->Report(request2_)); 248 | // Item cached, not flushed out 249 | EXPECT_EQ(flushed_.size(), 0); 250 | 251 | EXPECT_OK(aggregator_->FlushAll()); 252 | EXPECT_EQ(flushed_.size(), 1); 253 | EXPECT_TRUE(MessageDifferencer::Equals(flushed_[0], delta_merged12_)); 254 | } 255 | 256 | TEST_F(ReportAggregatorImplTest, TestCacheCapacity) { 257 | EXPECT_OK(aggregator_->Report(request1_)); 258 | // Item cached, not flushed out 259 | EXPECT_EQ(flushed_.size(), 0); 260 | 261 | // request2_ has different operation signature. Constrained by capacity 1, 262 | // request1 will be evicted from cache. 263 | AddLabel("key1", "value1", request2_.mutable_operations(0)); 264 | EXPECT_OK(aggregator_->Report(request2_)); 265 | // cache size is 1, the request1 has been flushed out. 266 | EXPECT_EQ(flushed_.size(), 1); 267 | EXPECT_TRUE(MessageDifferencer::Equals(flushed_[0], request1_)); 268 | 269 | EXPECT_OK(aggregator_->FlushAll()); 270 | EXPECT_EQ(flushed_.size(), 2); 271 | EXPECT_TRUE(MessageDifferencer::Equals(flushed_[1], request2_)); 272 | } 273 | 274 | TEST_F(ReportAggregatorImplTest, TestCacheExpiration) { 275 | EXPECT_OK(aggregator_->Report(request1_)); 276 | // Item cached, nothing flushed out 277 | EXPECT_EQ(flushed_.size(), 0); 278 | 279 | EXPECT_OK(aggregator_->Flush()); 280 | // Not expired yet, nothing flush out. 281 | EXPECT_EQ(flushed_.size(), 0); 282 | 283 | // sleep 1.2 second. 284 | usleep(1200000); 285 | EXPECT_OK(aggregator_->Flush()); 286 | // Item should be expired now. 287 | EXPECT_EQ(flushed_.size(), 1); 288 | EXPECT_TRUE(MessageDifferencer::Equals(flushed_[0], request1_)); 289 | } 290 | 291 | TEST_F(ReportAggregatorImplTest, TestHighValueOperationSuccess) { 292 | request1_.mutable_operations(0)->set_importance(Operation::HIGH); 293 | EXPECT_EQ(absl::StatusCode::kNotFound, aggregator_->Report(request1_).code()); 294 | 295 | // Nothing flush out. 296 | EXPECT_EQ(flushed_.size(), 0); 297 | // Nothing in the cache 298 | EXPECT_OK(aggregator_->FlushAll()); 299 | EXPECT_EQ(flushed_.size(), 0); 300 | } 301 | 302 | TEST_F(ReportAggregatorImplTest, TestDisableCache) { 303 | ReportAggregationOptions options(0 /*entries*/, 1000 /*flush_interval_ms*/); 304 | aggregator_ = 305 | CreateReportAggregator(kServiceName, kServiceConfigId, options, 306 | std::shared_ptr(new MetricKindMap)); 307 | ASSERT_TRUE((bool)(aggregator_)); 308 | aggregator_->SetFlushCallback(std::bind( 309 | &ReportAggregatorImplTest::FlushCallback, this, std::placeholders::_1)); 310 | 311 | EXPECT_EQ(absl::StatusCode::kNotFound, aggregator_->Report(request1_).code()); 312 | // Nothing flush out. 313 | EXPECT_EQ(flushed_.size(), 0); 314 | // Nothing in the cache 315 | EXPECT_OK(aggregator_->FlushAll()); 316 | EXPECT_EQ(flushed_.size(), 0); 317 | } 318 | 319 | TEST_F(ReportAggregatorImplTest, TestFlushAllWithCallbackCallingFlush) { 320 | aggregator_->SetFlushCallback( 321 | std::bind(&ReportAggregatorImplTest::FlushCallbackCallingBackToAggregator, 322 | this, std::placeholders::_1)); 323 | 324 | EXPECT_OK(aggregator_->Report(request1_)); 325 | EXPECT_OK(aggregator_->FlushAll()); 326 | EXPECT_EQ(flushed_.size(), 1); 327 | } 328 | 329 | TEST_F(ReportAggregatorImplTest, TestReportWithCallbackCallingFlush) { 330 | aggregator_->SetFlushCallback( 331 | std::bind(&ReportAggregatorImplTest::FlushCallbackCallingBackToAggregator, 332 | this, std::placeholders::_1)); 333 | 334 | EXPECT_OK(aggregator_->Report(request1_)); 335 | AddLabel("key1", "value1", request2_.mutable_operations(0)); 336 | EXPECT_OK(aggregator_->Report(request2_)); 337 | // Report(request2_) will evict request1_ out since the cacahe capacity is 1. 338 | EXPECT_EQ(flushed_.size(), 1); 339 | EXPECT_TRUE(MessageDifferencer::Equals(flushed_[0], request1_)); 340 | 341 | EXPECT_OK(aggregator_->FlushAll()); 342 | EXPECT_EQ(flushed_.size(), 2); 343 | EXPECT_TRUE(MessageDifferencer::Equals(flushed_[1], request2_)); 344 | } 345 | 346 | } // namespace service_control_client 347 | } // namespace google 348 | -------------------------------------------------------------------------------- /src/service_control_client_factory_impl.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2021 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_SERVICE_CONTROL_CLIENT_FACTORY_IMPL_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_SERVICE_CONTROL_CLIENT_FACTORY_IMPL_H_ 18 | 19 | #include "include/service_control_client.h" 20 | #include "include/service_control_client_factory.h" 21 | 22 | namespace google { 23 | namespace service_control_client { 24 | 25 | class ServiceControlClientFactoryImpl : public ServiceControlClientFactory { 26 | public: 27 | ServiceControlClientFactoryImpl() {} 28 | ~ServiceControlClientFactoryImpl() override {} 29 | 30 | std::unique_ptr 31 | CreateClient(const std::string &service_name, 32 | const std::string &service_config_id, 33 | ServiceControlClientOptions &options) const override { 34 | return CreateServiceControlClient(service_name, service_config_id, options); 35 | } 36 | }; 37 | 38 | } // namespace service_control_client 39 | } // namespace google 40 | 41 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_SERVICE_CONTROL_CLIENT_FACTORY_IMPL_H_ 42 | -------------------------------------------------------------------------------- /src/service_control_client_impl.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_SERVICE_CONTROL_CLIENT_IMPL_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_SERVICE_CONTROL_CLIENT_IMPL_H_ 18 | 19 | #include 20 | 21 | #include "absl/status/status.h" 22 | #include "include/service_control_client.h" 23 | #include "src/quota_aggregator_impl.h" 24 | #include "utils/google_macros.h" 25 | 26 | namespace google { 27 | namespace service_control_client { 28 | 29 | // ServiceControlClient implementation class. 30 | // Thread safe. 31 | class ServiceControlClientImpl : public ServiceControlClient { 32 | public: 33 | // Constructor. 34 | ServiceControlClientImpl(const std::string &service_name, 35 | const std::string &service_config_id, 36 | ServiceControlClientOptions &options); 37 | 38 | // Override the destructor. 39 | virtual ~ServiceControlClientImpl(); 40 | 41 | // An async check call. 42 | virtual void 43 | Check(const ::google::api::servicecontrol::v1::CheckRequest &check_request, 44 | ::google::api::servicecontrol::v1::CheckResponse *check_response, 45 | DoneCallback on_check_done); 46 | 47 | // A check call with per_request transport. 48 | virtual void 49 | Check(const ::google::api::servicecontrol::v1::CheckRequest &check_request, 50 | ::google::api::servicecontrol::v1::CheckResponse *check_response, 51 | DoneCallback on_check_done, TransportCheckFunc check_transport); 52 | 53 | // An async quota call. 54 | virtual void Quota( 55 | const ::google::api::servicecontrol::v1::AllocateQuotaRequest 56 | "a_request, 57 | ::google::api::servicecontrol::v1::AllocateQuotaResponse *quota_response, 58 | DoneCallback on_quota_done); 59 | 60 | // A quota call with per_request transport. 61 | virtual void Quota( 62 | const ::google::api::servicecontrol::v1::AllocateQuotaRequest 63 | "a_request, 64 | ::google::api::servicecontrol::v1::AllocateQuotaResponse *quota_response, 65 | DoneCallback on_quota_done, TransportQuotaFunc quota_transport); 66 | 67 | // An async report call. 68 | virtual void 69 | Report(const ::google::api::servicecontrol::v1::ReportRequest &report_request, 70 | ::google::api::servicecontrol::v1::ReportResponse *report_response, 71 | DoneCallback on_report_done); 72 | 73 | virtual absl::Status GetStatistics(Statistics *stat) const; 74 | 75 | // A report call with per_request transport. 76 | virtual void 77 | Report(const ::google::api::servicecontrol::v1::ReportRequest &report_request, 78 | ::google::api::servicecontrol::v1::ReportResponse *report_response, 79 | DoneCallback on_report_done, TransportReportFunc report_transport); 80 | 81 | private: 82 | absl::Status convertResponseStatus( 83 | const ::google::api::servicecontrol::v1::AllocateQuotaResponse &response); 84 | 85 | // A flush callback for check. 86 | void CheckFlushCallback( 87 | const ::google::api::servicecontrol::v1::CheckRequest &check_request); 88 | 89 | // A flush callback for check. 90 | void AllocateQuotaFlushCallback( 91 | const ::google::api::servicecontrol::v1::AllocateQuotaRequest 92 | "a_request); 93 | 94 | // A flush callback for report. 95 | void ReportFlushCallback( 96 | const ::google::api::servicecontrol::v1::ReportRequest &report_request); 97 | 98 | // Gets next flush interval 99 | int GetNextFlushInterval(); 100 | 101 | // Flushes out all items. 102 | absl::Status FlushAll(); 103 | 104 | // Flushes out expired items. 105 | absl::Status Flush(); 106 | 107 | std::string service_name_; 108 | 109 | // The check transport function. 110 | TransportQuotaFunc quota_transport_; 111 | // The check transport function. 112 | TransportCheckFunc check_transport_; 113 | // The report transport function. 114 | TransportReportFunc report_transport_; 115 | 116 | // The Timer object. 117 | std::shared_ptr flush_timer_; 118 | 119 | // Atomic object to deal with multi-threads situation. 120 | std::atomic_int_fast64_t total_called_quotas_; 121 | std::atomic_int_fast64_t send_quotas_by_flush_; 122 | std::atomic_int_fast64_t send_quotas_in_flight_; 123 | 124 | std::atomic_int_fast64_t total_called_checks_; 125 | std::atomic_int_fast64_t send_checks_by_flush_; 126 | std::atomic_int_fast64_t send_checks_in_flight_; 127 | 128 | std::atomic_int_fast64_t total_called_reports_; 129 | std::atomic_int_fast64_t send_reports_by_flush_; 130 | std::atomic_int_fast64_t send_reports_in_flight_; 131 | std::atomic_int_fast64_t send_report_operations_; 132 | 133 | // The check aggregator object. Uses shared_ptr for check_aggregator_. 134 | // Transport::on_check_done() callback needs to call check_aggregator_ 135 | // CacheResponse() function. The callback function needs to hold a ref_count 136 | // of check_aggregator_ to make sure it is not freed. 137 | std::shared_ptr check_aggregator_; 138 | 139 | std::shared_ptr quota_aggregator_; 140 | 141 | // The report aggregator object. report_aggregator_ has to be shared_ptr since 142 | // it will be passed to flush_timer callback. 143 | std::shared_ptr report_aggregator_; 144 | 145 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceControlClientImpl); 146 | }; 147 | 148 | } // namespace service_control_client 149 | } // namespace google 150 | 151 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_SERVICE_CONTROL_CLIENT_IMPL_H_ 152 | -------------------------------------------------------------------------------- /src/signature.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "src/signature.h" 17 | 18 | #include 19 | #include 20 | 21 | #include "utils/md5.h" 22 | 23 | using google::api::servicecontrol::v1::AllocateQuotaRequest; 24 | using google::api::servicecontrol::v1::CheckRequest; 25 | using google::api::servicecontrol::v1::MetricValue; 26 | using google::api::servicecontrol::v1::Operation; 27 | using google::api::servicecontrol::v1::QuotaOperation; 28 | using std::string; 29 | 30 | namespace google { 31 | namespace service_control_client { 32 | namespace { 33 | const char kDelimiter[] = "\0"; 34 | const int kDelimiterLength = 1; 35 | 36 | // Updates the give hasher with the given labels. 37 | void UpdateHashLabels(const ::google::protobuf::Map& labels, 38 | MD5* hasher) { 39 | std::map ordered_labels(labels.begin(), labels.end()); 40 | for (const auto& label : ordered_labels) { 41 | // Note we must use the Update(void const *data, int size) function here 42 | // for the delimiter instead of Update(StringPiece data), because 43 | // StringPiece would use strlen and gets zero length. 44 | hasher->Update(kDelimiter, kDelimiterLength); 45 | hasher->Update(label.first); 46 | hasher->Update(kDelimiter, kDelimiterLength); 47 | hasher->Update(label.second); 48 | } 49 | } 50 | 51 | // Updates the give hasher with the given metric value. 52 | void UpdateHashMetricValue(const MetricValue& metric_value, MD5* hasher) { 53 | UpdateHashLabels(metric_value.labels(), hasher); 54 | } 55 | } // namespace 56 | 57 | string GenerateReportOperationSignature(const Operation& operation) { 58 | MD5 hasher; 59 | hasher.Update(operation.consumer_id()); 60 | hasher.Update(kDelimiter, kDelimiterLength); 61 | hasher.Update(operation.operation_name()); 62 | 63 | UpdateHashLabels(operation.labels(), &hasher); 64 | 65 | return hasher.Digest(); 66 | } 67 | 68 | string GenerateReportMetricValueSignature(const MetricValue& metric_value) { 69 | MD5 hasher; 70 | 71 | UpdateHashMetricValue(metric_value, &hasher); 72 | return hasher.Digest(); 73 | } 74 | 75 | string GenerateCheckRequestSignature(const CheckRequest& request) { 76 | MD5 hasher; 77 | 78 | const Operation& operation = request.operation(); 79 | hasher.Update(operation.operation_name()); 80 | 81 | hasher.Update(kDelimiter, kDelimiterLength); 82 | hasher.Update(operation.consumer_id()); 83 | 84 | hasher.Update(kDelimiter, kDelimiterLength); 85 | UpdateHashLabels(operation.labels(), &hasher); 86 | 87 | // keep sorted order of metric_name 88 | std::map*> 90 | metric_value_set_map; 91 | for (const auto& metric_value_set : operation.metric_value_sets()) { 92 | metric_value_set_map[metric_value_set.metric_name()] = 93 | &metric_value_set.metric_values(); 94 | } 95 | 96 | for (const auto& metric_value_set : metric_value_set_map) { 97 | hasher.Update(kDelimiter, kDelimiterLength); 98 | hasher.Update(metric_value_set.first); 99 | 100 | for (const auto& metric_value : *metric_value_set.second) { 101 | UpdateHashMetricValue(metric_value, &hasher); 102 | } 103 | } 104 | 105 | hasher.Update(kDelimiter, kDelimiterLength); 106 | 107 | return hasher.Digest(); 108 | } 109 | 110 | string GenerateAllocateQuotaRequestSignature( 111 | const AllocateQuotaRequest& request) { 112 | MD5 hasher; 113 | const QuotaOperation& operation = request.allocate_operation(); 114 | hasher.Update(operation.method_name()); 115 | 116 | hasher.Update(kDelimiter, kDelimiterLength); 117 | hasher.Update(operation.consumer_id()); 118 | 119 | // order of metric_name can be changed. need to be sorted. 120 | std::set metric_names; 121 | for (const auto& metric_value_set : operation.quota_metrics()) { 122 | metric_names.insert(metric_value_set.metric_name()); 123 | } 124 | 125 | for (const auto& metric_name : metric_names) { 126 | hasher.Update(kDelimiter, kDelimiterLength); 127 | hasher.Update(metric_name); 128 | } 129 | return hasher.Digest(); 130 | } 131 | 132 | } // namespace service_control_client 133 | } // namespace google 134 | -------------------------------------------------------------------------------- /src/signature.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // Utility functions used to generate signature for operations, metric values, 17 | // and check requests. 18 | 19 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_SIGNATURE_H_ 20 | #define GOOGLE_SERVICE_CONTROL_CLIENT_SIGNATURE_H_ 21 | 22 | #include 23 | 24 | #include "google/api/servicecontrol/v1/metric_value.pb.h" 25 | #include "google/api/servicecontrol/v1/operation.pb.h" 26 | #include "google/api/servicecontrol/v1/quota_controller.pb.h" 27 | #include "google/api/servicecontrol/v1/service_controller.pb.h" 28 | 29 | namespace google { 30 | namespace service_control_client { 31 | 32 | // Generates signature for an operation based on operation name and operation 33 | // labels. Should be used only for report requests. 34 | // 35 | // Operations having the same signature can be aggregated or batched. Assuming 36 | // all operations belong to the same service. 37 | std::string GenerateReportOperationSignature( 38 | const ::google::api::servicecontrol::v1::Operation &operation); 39 | 40 | // Generates signature for a metric value based on metric value labels, and 41 | // currency code(For money value only). Should be used only for report requests. 42 | // 43 | // metric value with the same metric name and metric value signature can be 44 | // merged. 45 | std::string GenerateReportMetricValueSignature( 46 | const ::google::api::servicecontrol::v1::MetricValue &metric_value); 47 | 48 | // Generates signature for a check request. Operation name, consumer id, 49 | // operation labels, metric name, metric value labels, currency code(For money 50 | // value only), quota properties, and request project settings are all included 51 | // to generate the signature. 52 | // 53 | // Check request having the same signature can be aggregated. Assuming all 54 | // requests belong to the same service. 55 | std::string GenerateCheckRequestSignature( 56 | const ::google::api::servicecontrol::v1::CheckRequest &request); 57 | 58 | std::string GenerateAllocateQuotaRequestSignature( 59 | const ::google::api::servicecontrol::v1::AllocateQuotaRequest &request); 60 | 61 | } // namespace service_control_client 62 | } // namespace google 63 | 64 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_SIGNATURE_H_ 65 | -------------------------------------------------------------------------------- /src/signature_test.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "src/signature.h" 17 | 18 | #include "google/protobuf/text_format.h" 19 | #include "gtest/gtest.h" 20 | #include "utils/md5.h" 21 | 22 | using ::google::api::servicecontrol::v1::CheckRequest; 23 | using ::google::api::servicecontrol::v1::MetricValue; 24 | using ::google::api::servicecontrol::v1::Operation; 25 | using ::google::protobuf::TextFormat; 26 | using std::string; 27 | 28 | namespace google { 29 | namespace service_control_client { 30 | namespace { 31 | 32 | const char kRegionLabel[] = "cloud.googleapis.com/region"; 33 | const char kResourceTypeLabel[] = "cloud.googleapis.com/resource_type"; 34 | 35 | const char kCustomLabel[] = "/type"; 36 | 37 | const char kCheckRequest[] = R"( 38 | service_name: "chemisttest.googleapis.com" 39 | operation { 40 | operation_id: "some-id" 41 | operation_name: "some-operation-name" 42 | consumer_id: "project:proven-catcher-789" 43 | start_time { 44 | seconds: 1421429665 45 | nanos: 653927303 46 | } 47 | end_time { 48 | seconds: 1421429665 49 | nanos: 653933882 50 | } 51 | labels { 52 | key: "cloud.googleapis.com/resource_type" 53 | value: "some-resource_type" 54 | } 55 | labels { 56 | key: "cloud.googleapis.com/zone" 57 | value: "us-central1-a" 58 | } 59 | labels { 60 | key: "cloud.googleapis.com/resource_id" 61 | value: "some-resource_id" 62 | } 63 | metric_value_sets { 64 | metric_name: "chemisttest.googleapis.com/chemisttest/memory_hour_usage" 65 | metric_values { 66 | labels { 67 | key: "servicecontrol.googleapis.com/consumer_id" 68 | value: "some-consumer_id" 69 | } 70 | start_time { 71 | seconds: 1421429365 72 | nanos: 340407982 73 | } 74 | end_time { 75 | seconds: 1421429665 76 | nanos: 654013127 77 | } 78 | int64_value: 380 79 | } 80 | } 81 | } 82 | )"; 83 | 84 | class SignatureUtilTest : public ::testing::Test { 85 | protected: 86 | SignatureUtilTest() { 87 | operation_.set_operation_name("some-operation-name"); 88 | operation_.set_consumer_id("project_id:some-project-id"); 89 | } 90 | 91 | void AddOperationLabel(const string& key, const string& value, 92 | Operation* operation) { 93 | (*operation->mutable_labels())[key] = value; 94 | 95 | metric_value_.set_int64_value(1000); 96 | } 97 | 98 | void AddMetricValueLabel(const string& key, const string& value, 99 | MetricValue* metric_value) { 100 | (*metric_value->mutable_labels())[key] = value; 101 | } 102 | 103 | Operation operation_; 104 | 105 | MetricValue metric_value_; 106 | }; 107 | 108 | TEST_F(SignatureUtilTest, OperationWithNoLabel) { 109 | EXPECT_EQ("d056b16b88b914b40cd5a82470bc02a5", 110 | MD5::DebugString(GenerateReportOperationSignature(operation_))); 111 | } 112 | 113 | TEST_F(SignatureUtilTest, OperationWithLabels) { 114 | AddOperationLabel(kRegionLabel, "us-central1", &operation_); 115 | AddOperationLabel(kResourceTypeLabel, "instance", &operation_); 116 | 117 | EXPECT_EQ("93bc5c613fc4eabb2a40042f7f73f671", 118 | MD5::DebugString(GenerateReportOperationSignature(operation_))); 119 | } 120 | 121 | TEST_F(SignatureUtilTest, MetricValueWithNoLabel) { 122 | EXPECT_EQ( 123 | "d41d8cd98f00b204e9800998ecf8427e", 124 | MD5::DebugString(GenerateReportMetricValueSignature(metric_value_))); 125 | } 126 | 127 | TEST_F(SignatureUtilTest, MetricValueWithLabels) { 128 | AddMetricValueLabel(kCustomLabel, "disk", &metric_value_); 129 | 130 | EXPECT_EQ( 131 | "3f6bc74c0a4be6b6eeaab1faac30a365", 132 | MD5::DebugString(GenerateReportMetricValueSignature(metric_value_))); 133 | } 134 | 135 | TEST_F(SignatureUtilTest, CheckRequest) { 136 | CheckRequest request; 137 | ASSERT_TRUE(TextFormat::ParseFromString(kCheckRequest, &request)); 138 | EXPECT_EQ("4deb431384f1dbb616b59e00db496347", 139 | MD5::DebugString(GenerateCheckRequestSignature(request))); 140 | } 141 | 142 | } // namespace 143 | } // namespace service_control_client 144 | } // namespace google 145 | -------------------------------------------------------------------------------- /test/mocks.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2021 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_MOCKS_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_MOCKS_H_ 18 | 19 | #include "include/service_control_client.h" 20 | #include "include/service_control_client_factory.h" 21 | #include "gmock/gmock.h" 22 | 23 | namespace google { 24 | namespace service_control_client { 25 | namespace testing { 26 | 27 | class MockServiceControlClient : public ServiceControlClient { 28 | public: 29 | MOCK_METHOD( 30 | void, Check, 31 | (const ::google::api::servicecontrol::v1::CheckRequest &check_request, 32 | ::google::api::servicecontrol::v1::CheckResponse *check_response, 33 | DoneCallback on_check_done)); 34 | 35 | MOCK_METHOD( 36 | void, Check, 37 | (const ::google::api::servicecontrol::v1::CheckRequest &check_request, 38 | ::google::api::servicecontrol::v1::CheckResponse *check_response, 39 | DoneCallback on_check_done, TransportCheckFunc check_transport)); 40 | 41 | MOCK_METHOD( 42 | void, Quota, 43 | (const ::google::api::servicecontrol::v1::AllocateQuotaRequest 44 | "a_request, 45 | ::google::api::servicecontrol::v1::AllocateQuotaResponse *quota_response, 46 | DoneCallback on_quota_done)); 47 | 48 | MOCK_METHOD( 49 | void, Quota, 50 | (const ::google::api::servicecontrol::v1::AllocateQuotaRequest 51 | "a_request, 52 | ::google::api::servicecontrol::v1::AllocateQuotaResponse *quota_response, 53 | DoneCallback on_quota_done, TransportQuotaFunc quota_transport)); 54 | 55 | MOCK_METHOD( 56 | void, Report, 57 | (const ::google::api::servicecontrol::v1::ReportRequest &report_request, 58 | ::google::api::servicecontrol::v1::ReportResponse *report_response, 59 | DoneCallback on_report_done)); 60 | 61 | MOCK_METHOD( 62 | void, Report, 63 | (const ::google::api::servicecontrol::v1::ReportRequest &report_request, 64 | ::google::api::servicecontrol::v1::ReportResponse *report_response, 65 | DoneCallback on_report_done, TransportReportFunc report_transport)); 66 | 67 | MOCK_METHOD(absl::Status, GetStatistics, (Statistics * stat), (const)); 68 | }; 69 | 70 | class MockServiceControlClientFactory : public ServiceControlClientFactory { 71 | public: 72 | MOCK_METHOD(std::unique_ptr, CreateClient, 73 | (const std::string &service_name, 74 | const std::string &service_config_id, 75 | ServiceControlClientOptions &options), 76 | (const)); 77 | }; 78 | 79 | } // namespace testing 80 | } // namespace service_control_client 81 | } // namespace google 82 | 83 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_MOCKS_H_ 84 | -------------------------------------------------------------------------------- /test/mocks_test.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2021 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "mocks.h" 17 | 18 | #include "gtest/gtest.h" 19 | 20 | namespace google { 21 | namespace service_control_client { 22 | namespace { 23 | 24 | // Simple unit tests to ensure mocks compile (all virtual methods are mocked). 25 | TEST(MocksTest, MocksCompile) { 26 | testing::MockServiceControlClientFactory mock_factory; 27 | testing::MockServiceControlClient mock_client; 28 | } 29 | 30 | } // namespace 31 | } // namespace service_control_client 32 | } // namespace google 33 | -------------------------------------------------------------------------------- /utils/distribution_helper.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "distribution_helper.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | using ::google::api::servicecontrol::v1::Distribution; 24 | 25 | namespace google { 26 | namespace service_control_client { 27 | namespace { 28 | 29 | // Concatenate a string with an non string. 30 | template 31 | std::string StrCat(const char* str, T v) { 32 | std::ostringstream buffer; 33 | buffer.str(str); 34 | buffer << v; 35 | return buffer.str(); 36 | } 37 | 38 | // Updates general statistics other than bucket count. 39 | void UpdateGeneralStatictics(double value, Distribution* distribution) { 40 | if (distribution->count() == 0) { 41 | distribution->set_count(1); 42 | distribution->set_maximum(value); 43 | distribution->set_minimum(value); 44 | distribution->set_mean(value); 45 | distribution->set_sum_of_squared_deviation(0); 46 | } else { 47 | int64_t count = distribution->count(); 48 | double mean = distribution->mean(); 49 | double new_mean = (count * mean + value) / (count + 1); 50 | 51 | // The formula is optimized for minimum round off error (no large values in 52 | // intermediate results). 53 | distribution->set_sum_of_squared_deviation( 54 | distribution->sum_of_squared_deviation() + 55 | ((value - mean) * (value - new_mean))); 56 | 57 | distribution->set_count(count + 1); 58 | distribution->set_minimum(std::min(value, distribution->minimum())); 59 | distribution->set_maximum(std::max(value, distribution->maximum())); 60 | 61 | distribution->set_mean(new_mean); 62 | } 63 | } 64 | 65 | inline bool IsCloseEnough(double x, double y) { 66 | const double epsilon = 1e-5; 67 | return std::abs(x - y) <= epsilon * std::abs(x); 68 | } 69 | 70 | // Checks whether the bucket definitions in the two distributions are 71 | // approximately equal. We use this instead of 72 | // MessageDifferencer::ApproximatelyEquals mainly for performance reasons. 73 | bool BucketsApproximatelyEqual(const Distribution& first, 74 | const Distribution& second) { 75 | if (first.bucket_option_case() != second.bucket_option_case()) { 76 | return false; 77 | } 78 | switch (first.bucket_option_case()) { 79 | case Distribution::kLinearBuckets: { 80 | const auto& first_linear = first.linear_buckets(); 81 | const auto& second_linear = second.linear_buckets(); 82 | if (first_linear.num_finite_buckets() != 83 | second_linear.num_finite_buckets()) { 84 | return false; 85 | } 86 | if (!IsCloseEnough(first_linear.width(), second_linear.width())) { 87 | return false; 88 | } 89 | if (!IsCloseEnough(first_linear.offset(), second_linear.offset())) { 90 | return false; 91 | } 92 | break; 93 | } 94 | case Distribution::kExponentialBuckets: { 95 | const auto& first_exponential = first.exponential_buckets(); 96 | const auto& second_exponential = second.exponential_buckets(); 97 | if (first_exponential.num_finite_buckets() != 98 | second_exponential.num_finite_buckets()) { 99 | return false; 100 | } 101 | if (!IsCloseEnough(first_exponential.growth_factor(), 102 | second_exponential.growth_factor())) { 103 | return false; 104 | } 105 | if (!IsCloseEnough(first_exponential.scale(), 106 | second_exponential.scale())) { 107 | return false; 108 | } 109 | break; 110 | } 111 | case Distribution::kExplicitBuckets: { 112 | const auto& first_explicit = first.explicit_buckets(); 113 | const auto& second_explicit = second.explicit_buckets(); 114 | if (first_explicit.bounds_size() != second_explicit.bounds_size()) { 115 | return false; 116 | } 117 | for (int i = 0; i < first_explicit.bounds_size(); ++i) { 118 | if (!IsCloseEnough(first_explicit.bounds(i), 119 | second_explicit.bounds(i))) { 120 | return false; 121 | } 122 | } 123 | break; 124 | } 125 | default: 126 | return false; 127 | } 128 | return true; 129 | } 130 | 131 | void UpdateExponentialBucketCount(double value, Distribution* distribution) { 132 | const auto& exponential = distribution->exponential_buckets(); 133 | int bucket_index = 0; 134 | if (value >= exponential.scale()) { 135 | // Should be put into bucket bucket_index, starting from 0. 136 | bucket_index = 1 + static_cast(log2(value / exponential.scale()) / 137 | log2(exponential.growth_factor())); 138 | if (bucket_index > exponential.num_finite_buckets() + 1) { 139 | bucket_index = exponential.num_finite_buckets() + 1; 140 | } 141 | } 142 | distribution->set_bucket_counts( 143 | bucket_index, distribution->bucket_counts(bucket_index) + 1); 144 | } 145 | 146 | void UpdateLinearBucketCount(double value, Distribution* distribution) { 147 | const auto& linear = distribution->linear_buckets(); 148 | double upper_bound = 149 | linear.offset() + linear.num_finite_buckets() * linear.width(); 150 | double lower_bound = linear.offset(); 151 | 152 | int bucket_index; 153 | if (value < lower_bound || std::isnan(value)) { 154 | bucket_index = 0; 155 | } else if (value >= upper_bound) { 156 | bucket_index = linear.num_finite_buckets() + 1; 157 | } else { 158 | bucket_index = 1 + static_cast((value - lower_bound) / linear.width()); 159 | } 160 | 161 | distribution->set_bucket_counts( 162 | bucket_index, distribution->bucket_counts(bucket_index) + 1); 163 | } 164 | 165 | void UpdateExplicitBucketCount(double value, Distribution* distribution) { 166 | const auto& bounds = distribution->explicit_buckets().bounds(); 167 | int bucket_index = 0; 168 | if (value >= bounds.Get(0)) { 169 | // -inf < b0 < b1 < b2 < b3 < +inf (4 values in "bounds") 170 | // | 0 | 1 | 2 | 3 | 4 | (5 buckets) 171 | // std::upper_bound returns the first value that is greater than given one. 172 | bucket_index = std::distance( 173 | bounds.begin(), std::upper_bound(bounds.begin(), bounds.end(), value)); 174 | } 175 | distribution->set_bucket_counts( 176 | bucket_index, distribution->bucket_counts(bucket_index) + 1); 177 | } 178 | 179 | } // namespace 180 | 181 | absl::Status DistributionHelper::InitExponential(int num_finite_buckets, 182 | double growth_factor, double scale, 183 | Distribution* distribution) { 184 | if (num_finite_buckets <= 0) { 185 | return absl::Status(absl::StatusCode::kInvalidArgument, 186 | StrCat("Argument num_finite_buckets should be > 0. pass in: ", 187 | num_finite_buckets)); 188 | } 189 | if (growth_factor <= 1.0) { 190 | return absl::Status(absl::StatusCode::kInvalidArgument, 191 | StrCat("Argument growth_factor should be > 1.0. pass in: ", 192 | growth_factor)); 193 | } 194 | if (scale <= 0) { 195 | return absl::Status(absl::StatusCode::kInvalidArgument, 196 | StrCat("Argument scale should be > 0. pass in: ", scale)); 197 | } 198 | 199 | auto* exponential = distribution->mutable_exponential_buckets(); 200 | exponential->set_num_finite_buckets(num_finite_buckets); 201 | exponential->set_growth_factor(growth_factor); 202 | exponential->set_scale(scale); 203 | distribution->mutable_bucket_counts()->Resize(num_finite_buckets + 2, 0); 204 | return absl::OkStatus(); 205 | } 206 | 207 | absl::Status DistributionHelper::InitLinear(int num_finite_buckets, double width, 208 | double offset, 209 | Distribution* distribution) { 210 | if (num_finite_buckets <= 0) { 211 | return absl::Status (absl::StatusCode::kInvalidArgument, 212 | StrCat("Argument num_finite_buckets should be > 0. pass in: ", 213 | num_finite_buckets)); 214 | } 215 | if (width <= 0.0) { 216 | return absl::Status (absl::StatusCode::kInvalidArgument, 217 | StrCat("Argument width should be > 0.0. pass in: ", width)); 218 | } 219 | 220 | auto* linear = distribution->mutable_linear_buckets(); 221 | linear->set_num_finite_buckets(num_finite_buckets); 222 | linear->set_width(width); 223 | linear->set_offset(offset); 224 | distribution->mutable_bucket_counts()->Resize(num_finite_buckets + 2, 0); 225 | return absl::OkStatus(); 226 | } 227 | 228 | absl::Status DistributionHelper::InitExplicit(const std::vector& bounds, 229 | Distribution* distribution) { 230 | if (!std::is_sorted(bounds.begin(), bounds.end())) { 231 | return absl::Status (absl::StatusCode::kInvalidArgument, "Argument bounds should be sorted."); 232 | } 233 | if (std::adjacent_find(bounds.begin(), bounds.end()) != bounds.end()) { 234 | return absl::Status( 235 | absl::StatusCode::kInvalidArgument, 236 | "Two adjacent elements in argument bounds should NOT be the same."); 237 | } 238 | 239 | auto* explicit_buckets = distribution->mutable_explicit_buckets(); 240 | for (unsigned int i = 0; i < bounds.size(); ++i) { 241 | explicit_buckets->mutable_bounds()->Add(bounds.at(i)); 242 | } 243 | distribution->mutable_bucket_counts()->Resize(bounds.size() + 1, 0); 244 | return absl::OkStatus(); 245 | } 246 | 247 | absl::Status DistributionHelper::AddSample(double value, Distribution* distribution) { 248 | switch (distribution->bucket_option_case()) { 249 | case Distribution::kExponentialBuckets: 250 | UpdateGeneralStatictics(value, distribution); 251 | UpdateExponentialBucketCount(value, distribution); 252 | break; 253 | case Distribution::kLinearBuckets: 254 | UpdateGeneralStatictics(value, distribution); 255 | UpdateLinearBucketCount(value, distribution); 256 | break; 257 | case Distribution::kExplicitBuckets: 258 | UpdateGeneralStatictics(value, distribution); 259 | UpdateExplicitBucketCount(value, distribution); 260 | break; 261 | default: 262 | return absl::Status (absl::StatusCode::kInvalidArgument, 263 | StrCat("Unknown bucket option case: ", 264 | distribution->bucket_option_case())); 265 | } 266 | return absl::OkStatus(); 267 | } 268 | 269 | absl::Status DistributionHelper::Merge(const Distribution& from, Distribution* to) { 270 | if (!BucketsApproximatelyEqual(from, *to)) { 271 | return absl::Status (absl::StatusCode::kInvalidArgument, 272 | std::string("Bucket options don't match. From: ") + 273 | from.DebugString() + " to: " + to->DebugString()); 274 | } 275 | 276 | // TODO(chengliang): Make merging more tolerant here. 277 | if (from.bucket_counts_size() != to->bucket_counts_size()) { 278 | return absl::Status (absl::StatusCode::kInvalidArgument, "Bucket counts size don't match."); 279 | } 280 | 281 | if (from.count() <= 0) return absl::OkStatus(); 282 | if (to->count() <= 0) { 283 | *to = from; 284 | return absl::OkStatus(); 285 | } 286 | 287 | int64_t count = to->count(); 288 | double mean = to->mean(); 289 | double sum_of_squared_deviation = to->sum_of_squared_deviation(); 290 | 291 | to->set_count(to->count() + from.count()); 292 | to->set_minimum(std::min(from.minimum(), to->minimum())); 293 | to->set_maximum(std::max(from.maximum(), to->maximum())); 294 | to->set_mean((count * mean + from.count() * from.mean()) / to->count()); 295 | to->set_sum_of_squared_deviation( 296 | sum_of_squared_deviation + from.sum_of_squared_deviation() + 297 | count * (to->mean() - mean) * (to->mean() - mean) + 298 | from.count() * (to->mean() - from.mean()) * (to->mean() - from.mean())); 299 | 300 | for (int i = 0; i < from.bucket_counts_size(); i++) { 301 | to->set_bucket_counts(i, to->bucket_counts(i) + from.bucket_counts(i)); 302 | } 303 | return absl::OkStatus(); 304 | } 305 | 306 | } // namespace service_control_client 307 | } // namespace google 308 | -------------------------------------------------------------------------------- /utils/distribution_helper.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_DISTRIBUTION_HELPER_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_DISTRIBUTION_HELPER_H_ 18 | 19 | #include "absl/status/status.h" 20 | #include "google/api/servicecontrol/v1/distribution.pb.h" 21 | 22 | namespace google { 23 | namespace service_control_client { 24 | 25 | // A helper class for handling Distribution proto message. 26 | // Thread safe. 27 | class DistributionHelper final { 28 | public: 29 | // Inits the distribution with exponential buckets. 30 | static absl::Status InitExponential( 31 | int num_finite_buckets, double growth_factor, double scale, 32 | ::google::api::servicecontrol::v1::Distribution *distribution); 33 | 34 | // Inits the distribution with linear buckets. 35 | static absl::Status 36 | InitLinear(int num_finite_buckets, double width, double offset, 37 | ::google::api::servicecontrol::v1::Distribution *distribution); 38 | 39 | // Inits the distribution with explicit buckets. Note that bounds must be 40 | // sorted in ascending order and contain no duplicates. 41 | static absl::Status 42 | InitExplicit(const std::vector &bounds, 43 | ::google::api::servicecontrol::v1::Distribution *distribution); 44 | 45 | // Adds one more sample to the given distribution. 46 | static absl::Status 47 | AddSample(double value, 48 | ::google::api::servicecontrol::v1::Distribution *distribution); 49 | 50 | // Merges the "from" distribution to "to" distribution. 51 | // No change if the bucket options does not match. 52 | static absl::Status 53 | Merge(const ::google::api::servicecontrol::v1::Distribution &from, 54 | ::google::api::servicecontrol::v1::Distribution *to); 55 | }; 56 | 57 | } // namespace service_control_client 58 | } // namespace google 59 | 60 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_DISTRIBUTION_HELPER_H_ 61 | -------------------------------------------------------------------------------- /utils/google_macros.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_GOOGLE_MACROS_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_GOOGLE_MACROS_H_ 18 | 19 | #undef GOOGLE_DISALLOW_EVIL_CONSTRUCTORS 20 | #define GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ 21 | TypeName(const TypeName &); \ 22 | void operator=(const TypeName &) 23 | 24 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_GOOGLE_MACROS_H_ 25 | -------------------------------------------------------------------------------- /utils/md5.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "md5.h" 17 | #include 18 | 19 | namespace google { 20 | namespace service_control_client { 21 | 22 | MD5::MD5() : finalized_(false) { MD5_Init(&ctx_); } 23 | 24 | MD5& MD5::Update(const void* data, size_t size) { 25 | // Not update after finalized. 26 | assert(!finalized_); 27 | MD5_Update(&ctx_, data, size); 28 | return *this; 29 | } 30 | 31 | std::string MD5::Digest() { 32 | if (!finalized_) { 33 | MD5_Final(digest_, &ctx_); 34 | finalized_ = true; 35 | } 36 | return std::string(reinterpret_cast(digest_), kDigestLength); 37 | } 38 | 39 | std::string MD5::DebugString(const std::string& digest) { 40 | assert(digest.size() == kDigestLength); 41 | char buf[kDigestLength * 2 + 1]; 42 | char* p = buf; 43 | for (int i = 0; i < kDigestLength; i++, p += 2) { 44 | sprintf(p, "%02x", (unsigned char)digest[i]); 45 | } 46 | *p = 0; 47 | return std::string(buf, kDigestLength * 2); 48 | } 49 | 50 | std::string MD5::operator()(const void* data, size_t size) { 51 | return Update(data, size).Digest(); 52 | } 53 | 54 | } // namespace service_control_client 55 | } // namespace google 56 | -------------------------------------------------------------------------------- /utils/md5.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_MD5_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_MD5_H_ 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include "openssl/md5.h" 24 | 25 | namespace google { 26 | namespace service_control_client { 27 | 28 | // Define a MD5 Digest by calling OpenSSL 29 | class MD5 { 30 | public: 31 | MD5(); 32 | 33 | // Updates the context with data. 34 | MD5 &Update(const void *data, size_t size); 35 | 36 | // A helper function for const char* 37 | MD5 &Update(const char *str) { return Update(str, strlen(str)); } 38 | 39 | // A helper function for const string 40 | MD5 &Update(const std::string &str) { return Update(str.data(), str.size()); } 41 | 42 | // A helper function for int 43 | MD5 &Update(int d) { return Update(&d, sizeof(d)); } 44 | 45 | // The MD5 digest is always 128 bits = 16 bytes 46 | static const int kDigestLength = 16; 47 | 48 | // Returns the digest as string. 49 | std::string Digest(); 50 | 51 | // A short form of generating MD5 for a string 52 | std::string operator()(const void *data, size_t size); 53 | 54 | // Converts a binary digest string to a printable string. 55 | // It is for debugging and unit-test only. 56 | static std::string DebugString(const std::string &digest); 57 | 58 | private: 59 | // MD5 context. 60 | MD5_CTX ctx_; 61 | // The final MD5 digest. 62 | unsigned char digest_[kDigestLength]; 63 | // A flag to indicate if MD5_final is called or not. 64 | bool finalized_; 65 | }; 66 | 67 | } // namespace service_control_client 68 | } // namespace google 69 | 70 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_MD5_H_ 71 | -------------------------------------------------------------------------------- /utils/md5_test.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "md5.h" 17 | #include "gtest/gtest.h" 18 | 19 | namespace google { 20 | namespace service_control_client { 21 | namespace { 22 | 23 | // Unit tests to use CheckRequest protobuf message 24 | 25 | TEST(MD5Test, TestPriableGigest) { 26 | static const char data[] = "Test Data"; 27 | ASSERT_EQ("0a22b2ac9d829ff3605d81d5ae5e9d16", 28 | MD5::DebugString(MD5()(data, sizeof(data)))); 29 | } 30 | 31 | TEST(MD5Test, TestGigestEqual) { 32 | static const char data1[] = "Test Data1"; 33 | static const char data2[] = "Test Data2"; 34 | auto d1 = MD5()(data1, sizeof(data1)); 35 | auto d11 = MD5()(data1, sizeof(data1)); 36 | auto d2 = MD5()(data2, sizeof(data2)); 37 | ASSERT_EQ(d11, d1); 38 | ASSERT_NE(d1, d2); 39 | } 40 | 41 | } // namespace 42 | } // namespace service_control_client 43 | } // namespace google 44 | -------------------------------------------------------------------------------- /utils/simple_lru_cache.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // For inclusion in .h files. The real class definition is in 17 | // simple_lru_cache_inl.h. 18 | 19 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_SIMPLE_LRU_CACHE_H_ 20 | #define GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_SIMPLE_LRU_CACHE_H_ 21 | 22 | #include 23 | #include // for hash<> 24 | 25 | namespace google { 26 | namespace service_control_client { 27 | 28 | namespace internal { 29 | template struct SimpleLRUHash : public std::hash {}; 30 | } // namespace internal 31 | 32 | template , 34 | typename EQ = std::equal_to> 35 | class SimpleLRUCache; 36 | 37 | // Deleter is a functor that defines how to delete a Value*. That is, it 38 | // contains a public method: 39 | // operator() (Value* value) 40 | // See example in the associated unittest. 41 | template , 43 | typename EQ = std::equal_to> 44 | class SimpleLRUCacheWithDeleter; 45 | 46 | } // namespace service_control_client 47 | } // namespace google 48 | 49 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_SIMPLE_LRU_CACHE_H_ 50 | -------------------------------------------------------------------------------- /utils/status.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2022 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_STATUS_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_STATUS_H_ 18 | 19 | #include "absl/status/status.h" 20 | #include "google/rpc/status.pb.h" 21 | namespace google { 22 | namespace service_control_client { 23 | 24 | // This helper function converts absl:Status to google::rpc::Status. 25 | google::rpc::Status SaveStatusAsRpcStatus(const absl::Status &status) { 26 | google::rpc::Status ret; 27 | ret.set_code(static_cast(status.code())); 28 | ret.set_message(status.message().data(), status.message().size()); 29 | return ret; 30 | } 31 | 32 | } // namespace service_control_client 33 | } // namespace google 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /utils/status_test_util.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_STATUS_TEST_UTIL_H__ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_STATUS_TEST_UTIL_H__ 18 | 19 | #include "gmock/gmock.h" 20 | #include "gtest/gtest.h" 21 | 22 | // Macros for testing the results of functions that return absl::Status. 23 | #define EXPECT_OK(statement) EXPECT_EQ(absl::OkStatus(), (statement)) 24 | #define ASSERT_OK(statement) ASSERT_EQ(absl::OkStatus(), (statement)) 25 | 26 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_STATUS_TEST_UTIL_H__ 27 | -------------------------------------------------------------------------------- /utils/thread.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_THREAD_H_ 17 | #define GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_THREAD_H_ 18 | 19 | #include 20 | 21 | namespace google { 22 | namespace service_control_client { 23 | 24 | // Put all thread related dependencies in this header. 25 | // So they can be switched to use different packages. 26 | typedef std::mutex Mutex; 27 | typedef std::unique_lock MutexLock; 28 | 29 | } // namespace service_control_client 30 | } // namespace google 31 | 32 | #endif // GOOGLE_SERVICE_CONTROL_CLIENT_UTILS_THREAD_H_ 33 | --------------------------------------------------------------------------------