├── .editorconfig ├── .github └── workflows │ ├── build.yml │ ├── release.yml │ └── trigger-release.yml ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENSE ├── README.md ├── ci ├── .gitignore ├── after_success.sh ├── deploy-release.sh ├── deploy-snapshot.sh ├── dropwizard.asc.enc └── settings.xml ├── mvnw ├── pom.xml ├── renovate.json └── src ├── main ├── java │ └── io │ │ └── dropwizard │ │ └── redis │ │ ├── AbstractRedisClientFactory.java │ │ ├── RedisClientBundle.java │ │ ├── RedisClientFactory.java │ │ ├── RedisClusterClientBundle.java │ │ ├── RedisClusterClientFactory.java │ │ ├── clientoptions │ │ ├── ClientOptionsFactory.java │ │ └── ClusterClientOptionsFactory.java │ │ ├── clientresources │ │ ├── ClientResourcesFactory.java │ │ └── DefaultClientResourcesFactory.java │ │ ├── codec │ │ ├── ByteArrayCodecFactory.java │ │ ├── CompressionCodecFactory.java │ │ ├── RedisCodecFactory.java │ │ ├── StringCodecFactory.java │ │ └── Utf8StringCodecFactory.java │ │ ├── delay │ │ ├── ConstantDelayFactory.java │ │ ├── DecorrelatedJitterDelayFactory.java │ │ ├── DelayFactory.java │ │ ├── EqualJitterDelayFactory.java │ │ ├── ExponentialDelayFactory.java │ │ └── FullJitterDelayFactory.java │ │ ├── event │ │ ├── DefaultEventBusFactory.java │ │ ├── DefaultEventLoopGroupProviderFactory.java │ │ ├── EventBusFactory.java │ │ └── EventLoopGroupProviderFactory.java │ │ ├── health │ │ ├── Pingable.java │ │ └── RedisHealthCheck.java │ │ ├── managed │ │ └── RedisClientManager.java │ │ ├── metrics │ │ ├── CommandLatencyRecorderFactory.java │ │ ├── DefaultCommandLatencyCollectorFactory.java │ │ ├── DefaultEventPublisherOptionsFactory.java │ │ ├── DropwizardCommandLatencyRecorder.java │ │ ├── DropwizardCommandLatencyRecorderFactory.java │ │ ├── EventPublisherOptionsFactory.java │ │ └── event │ │ │ ├── LettuceMetricsSubscriber.java │ │ │ ├── visitor │ │ │ ├── ClusterTopologyChangedEventVisitor.java │ │ │ ├── CommandLatencyEventVisitor.java │ │ │ ├── ConnectedEventVisitor.java │ │ │ ├── ConnectionActivatedEventVisitor.java │ │ │ ├── ConnectionDeactivatedEventVisitor.java │ │ │ ├── DisconnectedEventVisitor.java │ │ │ └── EventVisitor.java │ │ │ └── wrapper │ │ │ ├── EventWrapperFactory.java │ │ │ ├── VisitableClusterTopologyEventWrapper.java │ │ │ ├── VisitableCommandLatencyEventWrapper.java │ │ │ ├── VisitableConnectedEventWrapper.java │ │ │ ├── VisitableConnectionActivatedEventWrapper.java │ │ │ ├── VisitableConnectionDeactivatedEventWrapper.java │ │ │ ├── VisitableDisconnectedEventWrapper.java │ │ │ └── VisitableEventWrapper.java │ │ ├── netty │ │ ├── DefaultEventExecutorGroupFactory.java │ │ └── EventExecutorGroupFactory.java │ │ ├── socket │ │ └── SocketOptionsFactory.java │ │ ├── ssl │ │ └── SslOptionsFactory.java │ │ ├── timeout │ │ └── TimeoutOptionsFactory.java │ │ ├── topology │ │ └── ClusterTopologyRefreshOptionsFactory.java │ │ └── uri │ │ ├── RedisModeURIFactory.java │ │ ├── RedisURIFactory.java │ │ └── SentinelModeURIFactory.java └── resources │ └── META-INF │ └── services │ ├── io.dropwizard.jackson.Discoverable │ ├── io.dropwizard.redis.AbstractRedisClientFactory │ ├── io.dropwizard.redis.clientresources.ClientResourcesFactory │ ├── io.dropwizard.redis.codec.RedisCodecFactory │ ├── io.dropwizard.redis.delay.DelayFactory │ ├── io.dropwizard.redis.event.EventBusFactory │ ├── io.dropwizard.redis.event.EventLoopGroupProviderFactory │ ├── io.dropwizard.redis.metrics.CommandLatencyRecorderFactory │ ├── io.dropwizard.redis.metrics.EventPublisherOptionsFactory │ ├── io.dropwizard.redis.netty.EventExecutorGroupFactory │ └── io.dropwizard.redis.uri.RedisURIFactory └── test ├── java └── io │ └── dropwizard │ └── redis │ ├── clientoptions │ └── ClusterClientOptionsFactoryTest.java │ ├── clientresources │ └── DefaultClientResourcesFactoryTest.java │ ├── codec │ ├── ByteArrayCodecFactoryTest.java │ ├── CompressionCodecFactoryTest.java │ ├── StringCodecFactoryTest.java │ └── Utf8StringCodecFactoryTest.java │ ├── delay │ ├── ConstantDelayFactoryTest.java │ ├── DecorrelatedJitterDelayFactoryTest.java │ ├── EqualJitterDelayFactoryTest.java │ ├── ExponentialDelayFactoryTest.java │ └── FullJitterDelayFactoryTest.java │ ├── event │ ├── DefaultEventBusFactoryTest.java │ └── DefaultEventLoopGroupProviderFactoryTest.java │ ├── health │ └── RedisHealthCheckTest.java │ ├── managed │ └── RedisClientManagerTest.java │ ├── metrics │ ├── DefaultCommandLatencyCollectorFactoryTest.java │ ├── DefaultEventPublisherOptionsFactoryTest.java │ ├── DropwizardCommandLatencyRecorderTest.java │ └── event │ │ └── LettuceMetricsSubscriberTest.java │ ├── netty │ └── DefaultEventExecutorGroupFactoryTest.java │ ├── socket │ └── SocketOptionsFactoryTest.java │ ├── ssl │ └── SslOptionsFactoryTest.java │ ├── test │ ├── RedisClusterBundleIT.java │ ├── TestApplication.java │ └── TestConfiguration.java │ ├── timeout │ └── TimeoutOptionsFactoryTest.java │ ├── topology │ └── ClusterTopologyRefreshOptionsFactoryTest.java │ └── uri │ ├── RedisModeURIFactoryTest.java │ └── SentinelModeURIFactoryTest.java └── resources ├── keystore.p12 ├── truststore.p12 └── yaml ├── clientresources └── default.yaml ├── codec └── compression.yaml ├── config.yaml ├── delay ├── constant.yaml ├── decorrelated-jitter.yaml ├── equal-jitter.yaml ├── exponential.yaml └── full-jitter.yaml ├── metrics ├── default-command-latency-collector.yaml └── default-event-publisher.yaml ├── netty └── default.yaml ├── socket └── socket-options.yaml ├── ssl └── ssl.yaml ├── timeout └── timeout-options.yaml ├── topology └── cluster-topology-refresh-options.yaml └── uri ├── redis-uri.yaml └── sentinel-redis-uri.yaml /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset=utf-8 3 | end_of_line=lf 4 | trim_trailing_whitespace=true 5 | max_line_length=120 6 | insert_final_newline=true 7 | indent_style=space 8 | indent_size=2 9 | 10 | [*.java] 11 | indent_style=space 12 | indent_size=4 13 | continuation_indent_size=8 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Build 3 | # yamllint disable-line rule:truthy 4 | on: 5 | push: 6 | branches: 7 | - release/* 8 | pull_request: 9 | jobs: 10 | yamllint: 11 | uses: dropwizard/workflows/.github/workflows/yamllint.yml@main 12 | build: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | java-version: ['11', '17', '21'] 17 | uses: dropwizard/workflows/.github/workflows/maven.yml@main 18 | secrets: inherit 19 | with: 20 | java-version: ${{ matrix.java-version }} 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | # yamllint disable-line rule:truthy 4 | on: 5 | push: 6 | tags: 7 | - dropwizard-redis-* 8 | jobs: 9 | release: 10 | uses: dropwizard/workflows/.github/workflows/release.yml@main 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.github/workflows/trigger-release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:comments rule:line-length 3 | name: Trigger Release 4 | # yamllint disable-line rule:truthy 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | releaseVersion: 9 | description: Version of the next release 10 | required: true 11 | type: string 12 | developmentVersion: 13 | description: Version of the next development cycle (must end in "-SNAPSHOT") 14 | required: true 15 | type: string 16 | jobs: 17 | release: 18 | uses: dropwizard/workflows/.github/workflows/trigger-release.yml@main 19 | secrets: inherit 20 | with: 21 | releaseVersion: ${{ inputs.releaseVersion }} 22 | developmentVersion: ${{ inputs.developmentVersion }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .out 4 | out 5 | .dist 6 | .run 7 | .gradle 8 | target 9 | logs 10 | 11 | .idea 12 | *.iml 13 | *.ipr 14 | *.iws 15 | *.class 16 | *.log 17 | 18 | # eclipse 19 | .project 20 | .settings 21 | bin 22 | .classpath 23 | 24 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 25 | hs_err_pid* 26 | 27 | */dependency-reduced-pom.xml 28 | node_modules 29 | .cache 30 | 31 | .vscode 32 | .factorypath 33 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropwizard/dropwizard-redis/420d22b093e8b78539ba2bdffd403bfc33517f69/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.10/apache-maven-3.9.10-bin.zip 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dropwizard-redis 2 | 3 | Provides easy integration for Dropwizard applications with Redis via the Lettuce library. 4 | 5 | This bundle comes with out-of-the-box support for: 6 | * Configuration 7 | * Client lifecycle management 8 | * Client health checks 9 | * Dropwizard Metrics integration 10 | * Brave distributed tracing instrumentation integration for the Lettuce client. 11 | * Support for the Lettuce cluster client. 12 | * Support for the Lettuce sentinel client 13 | * Support the the Lettuce basic Redis client 14 | 15 | For more information on Redis, take a look at the official documentation here: https://redis.io/documentation 16 | 17 | For More information on the Redis client used (Lettuce), see: https://github.com/lettuce-io/lettuce-core 18 | 19 | ## Dropwizard Version Support Matrix 20 | | dropwizard-redis | Dropwizard v1.3.x | Dropwizard v2.0.x | Dropwizard v2.1.x | Dropwizard v3.0.x | Dropwizard v4.0.x | 21 | |------------------|--------------------|--------------------|--------------------|--------------------|--------------------| 22 | | v1.3.x | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | 23 | | v1.4.x | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | 24 | | v1.5.x | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | 25 | | v1.6.x | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | 26 | | v1.7.x | :white_check_mark: | :white_check_mark: | :question: | :x: | :x: | 27 | | v1.8.x | :x: | :question: | :white_check_mark: | :x: | :x: | 28 | | v3.0.x | :x: | :x: | :x: | :white_check_mark: | :x: | 29 | | v4.0.x | :x: | :x: | :x: | :x: | :white_check_mark: | 30 | 31 | ## Usage 32 | Add dependency on library. 33 | 34 | Maven: 35 | ```xml 36 | 37 | io.dropwizard.modules 38 | dropwizard-redis 39 | $latest-version 40 | 41 | ``` 42 | 43 | Gradle: 44 | ```groovy 45 | compile "io.dropwizard.modules:dropwizard-redis:$latest-version" 46 | ``` 47 | 48 | 49 | ### Basic Lettuce Client 50 | In your Dropwizard `Configuration` class, configure a `RedisClientFactory`: 51 | ```java 52 | @Valid 53 | @NotNull 54 | @JsonProperty("redis") 55 | private RedisClientFactory redisClientFactory; 56 | ``` 57 | 58 | Then, in your `Application` class, you'll want to do something similar to the following: 59 | ```java 60 | private final RedisClientBundle redis = new RedisClientBundle() { 61 | @Override 62 | public RedisClientFactory getRedisClientFactory(ExampleConfiguration configuration) { 63 | return configuration.getRedisClientFactory(); 64 | } 65 | }; 66 | 67 | @Override 68 | public void initialize(Bootstrap bootstrap) { 69 | bootstrap.addBundle(redis); 70 | } 71 | 72 | @Override 73 | public void run(ExampleConfiguration config, Environment environment) { 74 | final StatefulRedisConnection connection = redisCluster.getConnection(); 75 | final PersonCache personCache = new PersonCache(connection); // PersonCache is an arbtirary example 76 | environment.jersey().register(new PersonResource(personCache)); 77 | } 78 | ``` 79 | 80 | 81 | ```yaml 82 | redis: 83 | type: basic 84 | name: my-redis-use-case 85 | node: 86 | type: redis 87 | node: "127.0.0.1:6379" 88 | clientName: person-app 89 | redisCodec: 90 | type: string 91 | clientResources: 92 | type: default 93 | commandLatencyRecorder: 94 | type: default 95 | enabled: false 96 | ``` 97 | 98 | 99 | ### Lettuce Cluster Client 100 | In your Dropwizard `Configuration` class, configure a `RedisClusterClientFactory`: 101 | ```java 102 | @Valid 103 | @NotNull 104 | @JsonProperty("redis-cluster") 105 | private RedisClusterClientFactory redisClientFactory; 106 | ``` 107 | 108 | Then, in your `Application` class, you'll want to do something similar to the following: 109 | ```java 110 | private final RedisClusterClientBundle redisCluster = new RedisClusterClientBundle() { 111 | @Override 112 | public RedisClusterClientFactory getRedisClusterClientFactory(ExampleConfiguration configuration) { 113 | return configuration.getRedisClusterClientFactory(); 114 | } 115 | }; 116 | 117 | @Override 118 | public void initialize(Bootstrap bootstrap) { 119 | bootstrap.addBundle(redisCluster); 120 | } 121 | 122 | @Override 123 | public void run(ExampleConfiguration config, Environment environment) { 124 | final StatefulRedisClusterConnection clusterConnection = redisCluster.getClusterConnection(); 125 | final PersonCache personCache = new PersonCache(clusterConnection); // PersonCache is an arbtirary example 126 | environment.jersey().register(new PersonResource(personCache)); 127 | } 128 | ``` 129 | 130 | Configure your factory in your `config.yml` file: 131 | ```yaml 132 | redis-cluster: 133 | type: cluster 134 | name: my-redis-cluster-use-case 135 | nodes: 136 | - type: redis 137 | node: "127.0.0.1:6379" 138 | clientName: person-app 139 | password: hunter2 140 | redisCodec: 141 | type: string 142 | clientResources: 143 | type: default 144 | commandLatencyRecorder: 145 | type: default 146 | enabled: false 147 | # TODO: add more configs than just the required basics 148 | ``` 149 | -------------------------------------------------------------------------------- /ci/.gitignore: -------------------------------------------------------------------------------- 1 | dropwizard.asc 2 | -------------------------------------------------------------------------------- /ci/after_success.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -uxo pipefail 4 | 5 | if [[ "${TRAVIS_JDK_VERSION}" == "openjdk8" ]]; then 6 | # ./mvnw coveralls:report -B -q 7 | # exit $? 8 | fi 9 | -------------------------------------------------------------------------------- /ci/deploy-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -uxo pipefail 4 | 5 | # Decrypt and import signing key 6 | openssl aes-256-cbc -K $encrypted_57a5d69ebeaa_key -iv $encrypted_57a5d69ebeaa_iv -in ci/dropwizard.asc.enc -out ci/dropwizard.asc -d 7 | gpg --armor --import ci/dropwizard.asc 8 | 9 | ./mvnw -B deploy --settings 'ci/settings.xml' -DperformRelease=true -Dmaven.test.skip=true 10 | -------------------------------------------------------------------------------- /ci/deploy-snapshot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -uxo pipefail 4 | 5 | ./mvnw -B deploy --settings 'ci/settings.xml' -Dmaven.test.skip=true 6 | -------------------------------------------------------------------------------- /ci/dropwizard.asc.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropwizard/dropwizard-redis/420d22b093e8b78539ba2bdffd403bfc33517f69/ci/dropwizard.asc.enc -------------------------------------------------------------------------------- /ci/settings.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | ossrh 8 | ${env.CI_DEPLOY_USERNAME} 9 | ${env.CI_DEPLOY_PASSWORD} 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # traverses directory structure from process work directory to filesystem root 188 | # first directory with .mvn subdirectory is considered project base directory 189 | find_maven_basedir() { 190 | local basedir=$(pwd) 191 | local wdir=$(pwd) 192 | while [ "$wdir" != '/' ] ; do 193 | if [ -d "$wdir"/.mvn ] ; then 194 | basedir=$wdir 195 | break 196 | fi 197 | wdir=$(cd "$wdir/.."; pwd) 198 | done 199 | echo "${basedir}" 200 | } 201 | 202 | # concatenates all lines of a file 203 | concat_lines() { 204 | if [ -f "$1" ]; then 205 | echo "$(tr -s '\n' ' ' < "$1")" 206 | fi 207 | } 208 | 209 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 210 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 211 | 212 | # For Cygwin, switch paths to Windows format before running java 213 | if $cygwin; then 214 | [ -n "$M2_HOME" ] && 215 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 216 | [ -n "$JAVA_HOME" ] && 217 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 218 | [ -n "$CLASSPATH" ] && 219 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 220 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 221 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 222 | fi 223 | 224 | # Provide a "standardized" way to retrieve the CLI args that will 225 | # work with both Windows and non-Windows executions. 226 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 227 | export MAVEN_CMD_LINE_ARGS 228 | 229 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 230 | 231 | # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@ 232 | exec "$JAVACMD" \ 233 | $MAVEN_OPTS \ 234 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 235 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 236 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 237 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | io.dropwizard.modules 7 | module-parent 8 | 4.0.4 9 | 10 | io.dropwizard.modules 11 | dropwizard-redis 12 | 4.0.1-SNAPSHOT 13 | jar 14 | 15 | Dropwizard Redis 16 | Provides easy integration for Dropwizard applications with Redis 17 | 18 | 19 | 20 | dropwizard-redis 21 | http://dropwizard.github.io/dropwizard-redis/${project.version} 22 | 23 | 24 | 25 | 26 | 27 | nzamani1 28 | Natalie Zamani 29 | natalie.zamani@apple.com 30 | 31 | 32 | 33 | 34 | dropwizard_dropwizard-redis 35 | 36 | 6.3.0 37 | 6.7.1.RELEASE 38 | 1.1.11 39 | 40 | 41 | 42 | 43 | io.dropwizard 44 | dropwizard-core 45 | 46 | 47 | io.lettuce 48 | lettuce-core 49 | ${lettuce.version} 50 | 51 | 52 | io.zipkin.brave 53 | brave 54 | ${brave.version} 55 | 56 | 57 | 58 | 59 | org.junit.jupiter 60 | junit-jupiter 61 | test 62 | 63 | 64 | org.mockito 65 | mockito-core 66 | test 67 | 68 | 69 | io.dropwizard 70 | dropwizard-testing 71 | test 72 | 73 | 74 | com.github.fppt 75 | jedis-mock 76 | ${redis-mock.version} 77 | test 78 | 79 | 80 | org.assertj 81 | assertj-core 82 | test 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | org.sonarsource.scanner.maven 91 | sonar-maven-plugin 92 | 5.1.0.4751 93 | 94 | 95 | org.jacoco 96 | jacoco-maven-plugin 97 | 0.8.13 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-compiler-plugin 105 | 3.14.0 106 | 107 | 8 108 | 109 | 110 | 111 | org.jacoco 112 | jacoco-maven-plugin 113 | 114 | 115 | prepare-agent 116 | 117 | prepare-agent 118 | 119 | 120 | 121 | report 122 | 123 | report 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | kr.motd.maven 132 | os-maven-plugin 133 | 1.7.1 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>dropwizard/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/AbstractRedisClientFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis; 2 | 3 | import brave.Tracing; 4 | import com.codahale.metrics.MetricRegistry; 5 | import com.codahale.metrics.health.HealthCheckRegistry; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 8 | import com.google.common.collect.ImmutableList; 9 | import io.dropwizard.jackson.Discoverable; 10 | import io.dropwizard.lifecycle.setup.LifecycleEnvironment; 11 | import io.dropwizard.redis.clientresources.ClientResourcesFactory; 12 | import io.dropwizard.redis.clientresources.DefaultClientResourcesFactory; 13 | import io.dropwizard.redis.codec.RedisCodecFactory; 14 | import io.dropwizard.redis.metrics.event.visitor.ClusterTopologyChangedEventVisitor; 15 | import io.dropwizard.redis.metrics.event.visitor.CommandLatencyEventVisitor; 16 | import io.dropwizard.redis.metrics.event.visitor.ConnectedEventVisitor; 17 | import io.dropwizard.redis.metrics.event.visitor.ConnectionActivatedEventVisitor; 18 | import io.dropwizard.redis.metrics.event.visitor.ConnectionDeactivatedEventVisitor; 19 | import io.dropwizard.redis.metrics.event.visitor.DisconnectedEventVisitor; 20 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 21 | import io.lettuce.core.api.StatefulConnection; 22 | import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; 23 | import org.checkerframework.checker.nullness.qual.Nullable; 24 | 25 | import java.util.List; 26 | 27 | import jakarta.validation.Valid; 28 | import jakarta.validation.constraints.NotNull; 29 | 30 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 31 | public abstract class AbstractRedisClientFactory implements Discoverable { 32 | @NotNull 33 | @JsonProperty 34 | protected String name; 35 | 36 | @Valid 37 | @JsonProperty 38 | protected ClusterTopologyRefreshOptions clusterTopologyRefreshOptions; 39 | 40 | @Valid 41 | @NotNull 42 | @JsonProperty 43 | protected ClientResourcesFactory clientResources = new DefaultClientResourcesFactory(); 44 | 45 | @Valid 46 | @NotNull 47 | @JsonProperty 48 | protected RedisCodecFactory redisCodec; 49 | 50 | public String getName() { 51 | return name; 52 | } 53 | 54 | public void setName(final String name) { 55 | this.name = name; 56 | } 57 | 58 | public ClusterTopologyRefreshOptions getClusterTopologyRefreshOptions() { 59 | return clusterTopologyRefreshOptions; 60 | } 61 | 62 | public void setClusterTopologyRefreshOptions(final ClusterTopologyRefreshOptions clusterTopologyRefreshOptions) { 63 | this.clusterTopologyRefreshOptions = clusterTopologyRefreshOptions; 64 | } 65 | 66 | public ClientResourcesFactory getClientResources() { 67 | return clientResources; 68 | } 69 | 70 | public void setClientResources(final ClientResourcesFactory clientResources) { 71 | this.clientResources = clientResources; 72 | } 73 | 74 | public RedisCodecFactory getRedisCodec() { 75 | return redisCodec; 76 | } 77 | 78 | public void setRedisCodec(final RedisCodecFactory redisCodec) { 79 | this.redisCodec = redisCodec; 80 | } 81 | 82 | public StatefulConnection build(final HealthCheckRegistry healthChecks, final LifecycleEnvironment lifecycle, 83 | final MetricRegistry metrics) { 84 | return build(healthChecks, lifecycle, metrics, null); 85 | } 86 | 87 | public abstract StatefulConnection build(final HealthCheckRegistry healthChecks, final LifecycleEnvironment lifecycle, 88 | final MetricRegistry metrics, @Nullable final Tracing tracing); 89 | 90 | protected List buildEventVisitors(final MetricRegistry metrics) { 91 | // Extract this, and the event wrapper builders, to Dropwizard factories, if more event types are added frequently enough? 92 | return ImmutableList.of( 93 | new ClusterTopologyChangedEventVisitor(name, metrics), 94 | new CommandLatencyEventVisitor(name, metrics), 95 | new ConnectedEventVisitor(name, metrics), 96 | new ConnectionActivatedEventVisitor(name, metrics), 97 | new ConnectionDeactivatedEventVisitor(name, metrics), 98 | new DisconnectedEventVisitor(name, metrics) 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/RedisClientBundle.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis; 2 | 3 | import brave.Tracing; 4 | import io.dropwizard.core.Configuration; 5 | import io.dropwizard.core.ConfiguredBundle; 6 | import io.dropwizard.core.setup.Bootstrap; 7 | import io.dropwizard.core.setup.Environment; 8 | import io.lettuce.core.api.StatefulRedisConnection; 9 | import org.checkerframework.checker.nullness.qual.Nullable; 10 | 11 | import static java.util.Objects.requireNonNull; 12 | 13 | public abstract class RedisClientBundle implements ConfiguredBundle { 14 | @Nullable 15 | private StatefulRedisConnection connection; 16 | 17 | @Override 18 | public void initialize(final Bootstrap bootstrap) { 19 | // do nothing 20 | } 21 | 22 | @Override 23 | public void run(final T configuration, final Environment environment) throws Exception { 24 | final RedisClientFactory redisClientFactory = requireNonNull(getRedisClientFactory(configuration)); 25 | 26 | final Tracing tracing = Tracing.current(); 27 | 28 | this.connection = redisClientFactory.build(environment.healthChecks(), environment.lifecycle(), environment.metrics(), tracing); 29 | } 30 | 31 | public abstract RedisClientFactory getRedisClientFactory(T configuration); 32 | 33 | public StatefulRedisConnection getConnection() { 34 | return requireNonNull(connection); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/RedisClientFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis; 2 | 3 | import brave.Tracing; 4 | import com.codahale.metrics.MetricRegistry; 5 | import com.codahale.metrics.health.HealthCheckRegistry; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.annotation.JsonTypeName; 8 | import io.dropwizard.lifecycle.setup.LifecycleEnvironment; 9 | import io.dropwizard.redis.clientoptions.ClientOptionsFactory; 10 | import io.dropwizard.redis.health.RedisHealthCheck; 11 | import io.dropwizard.redis.managed.RedisClientManager; 12 | import io.dropwizard.redis.metrics.event.LettuceMetricsSubscriber; 13 | import io.dropwizard.redis.uri.RedisURIFactory; 14 | import io.lettuce.core.RedisClient; 15 | import io.lettuce.core.RedisURI; 16 | import io.lettuce.core.api.StatefulRedisConnection; 17 | import io.lettuce.core.codec.RedisCodec; 18 | import io.lettuce.core.resource.ClientResources; 19 | 20 | import jakarta.validation.Valid; 21 | import jakarta.validation.constraints.NotNull; 22 | 23 | @JsonTypeName("basic") 24 | public class RedisClientFactory extends AbstractRedisClientFactory { 25 | @Valid 26 | @NotNull 27 | @JsonProperty 28 | private RedisURIFactory node; 29 | 30 | @Valid 31 | @NotNull 32 | @JsonProperty 33 | private ClientOptionsFactory clientOptions = new ClientOptionsFactory(); 34 | 35 | @Override 36 | public StatefulRedisConnection build(final HealthCheckRegistry healthChecks, final LifecycleEnvironment lifecycle, 37 | final MetricRegistry metrics) { 38 | return build(healthChecks, lifecycle, metrics, null); 39 | } 40 | 41 | @Override 42 | public StatefulRedisConnection build(final HealthCheckRegistry healthChecks, final LifecycleEnvironment lifecycle, 43 | final MetricRegistry metrics, final Tracing tracing) { 44 | final RedisURI uri = node.build(); 45 | 46 | final ClientResources resources = clientResources.build(name, metrics, tracing); 47 | 48 | final RedisClient redisClient = RedisClient.create(resources, uri); 49 | 50 | redisClient.setOptions(clientOptions.build()); 51 | 52 | final RedisCodec codec = redisCodec.build(); 53 | 54 | final StatefulRedisConnection connection = redisClient.connect(codec); 55 | 56 | // manage client and connection 57 | lifecycle.manage(new RedisClientManager(redisClient, connection, name)); 58 | 59 | // health check 60 | healthChecks.register(name, new RedisHealthCheck(() -> connection.sync().ping())); 61 | 62 | // metrics (latency and other connection events) integration 63 | redisClient.getResources() 64 | .eventBus() 65 | .get() 66 | .subscribe(new LettuceMetricsSubscriber(buildEventVisitors(metrics))); 67 | 68 | return connection; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/RedisClusterClientBundle.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis; 2 | 3 | import brave.Tracing; 4 | import io.dropwizard.core.Configuration; 5 | import io.dropwizard.core.ConfiguredBundle; 6 | import io.dropwizard.core.setup.Bootstrap; 7 | import io.dropwizard.core.setup.Environment; 8 | import io.lettuce.core.cluster.api.StatefulRedisClusterConnection; 9 | import org.checkerframework.checker.nullness.qual.Nullable; 10 | 11 | import static java.util.Objects.requireNonNull; 12 | 13 | public abstract class RedisClusterClientBundle implements ConfiguredBundle { 14 | @Nullable 15 | private StatefulRedisClusterConnection clusterConnection; 16 | 17 | @Override 18 | public void initialize(final Bootstrap bootstrap) { 19 | // do nothing 20 | } 21 | 22 | @Override 23 | public void run(final T configuration, final Environment environment) throws Exception { 24 | final RedisClusterClientFactory redisClusterClientFactory = requireNonNull(getRedisClusterClientFactory(configuration)); 25 | 26 | final Tracing tracing = Tracing.current(); 27 | 28 | this.clusterConnection = redisClusterClientFactory.build(environment.healthChecks(), environment.lifecycle(), environment.metrics(), 29 | tracing); 30 | } 31 | 32 | public abstract RedisClusterClientFactory getRedisClusterClientFactory(T configuration); 33 | 34 | public StatefulRedisClusterConnection getClusterConnection() { 35 | return requireNonNull(clusterConnection); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/RedisClusterClientFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis; 2 | 3 | import brave.Tracing; 4 | import com.codahale.metrics.MetricRegistry; 5 | import com.codahale.metrics.health.HealthCheckRegistry; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.annotation.JsonTypeName; 8 | import io.dropwizard.lifecycle.setup.LifecycleEnvironment; 9 | import io.dropwizard.redis.clientoptions.ClusterClientOptionsFactory; 10 | import io.dropwizard.redis.health.RedisHealthCheck; 11 | import io.dropwizard.redis.managed.RedisClientManager; 12 | import io.dropwizard.redis.metrics.event.LettuceMetricsSubscriber; 13 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 14 | import io.dropwizard.redis.uri.RedisURIFactory; 15 | import io.lettuce.core.RedisURI; 16 | import io.lettuce.core.cluster.RedisClusterClient; 17 | import io.lettuce.core.cluster.api.StatefulRedisClusterConnection; 18 | import io.lettuce.core.codec.RedisCodec; 19 | import io.lettuce.core.resource.ClientResources; 20 | import org.checkerframework.checker.nullness.qual.Nullable; 21 | import org.hibernate.validator.constraints.NotEmpty; 22 | 23 | import jakarta.validation.Valid; 24 | import jakarta.validation.constraints.NotNull; 25 | import java.util.Collections; 26 | import java.util.List; 27 | import java.util.stream.Collectors; 28 | 29 | @JsonTypeName("cluster") 30 | public class RedisClusterClientFactory extends AbstractRedisClientFactory { 31 | @Valid 32 | @NotEmpty 33 | @JsonProperty 34 | private List nodes = Collections.emptyList(); 35 | 36 | @Valid 37 | @NotNull 38 | @JsonProperty 39 | private ClusterClientOptionsFactory clientOptions = new ClusterClientOptionsFactory(); 40 | 41 | public ClusterClientOptionsFactory getClientOptions() { 42 | return clientOptions; 43 | } 44 | 45 | public void setClientOptions(final ClusterClientOptionsFactory clientOptions) { 46 | this.clientOptions = clientOptions; 47 | } 48 | 49 | @Override 50 | public StatefulRedisClusterConnection build(final HealthCheckRegistry healthChecks, final LifecycleEnvironment lifecycle, 51 | final MetricRegistry metrics) { 52 | return build(healthChecks, lifecycle, metrics, null); 53 | } 54 | 55 | @Override 56 | public StatefulRedisClusterConnection build(final HealthCheckRegistry healthChecks, final LifecycleEnvironment lifecycle, 57 | final MetricRegistry metrics, @Nullable final Tracing tracing) { 58 | final List uris = nodes.stream() 59 | .map(RedisURIFactory::build) 60 | .collect(Collectors.toList()); 61 | 62 | final ClientResources resources = clientResources.build(name, metrics, tracing); 63 | 64 | final RedisClusterClient redisClusterClient = RedisClusterClient.create(resources, uris); 65 | 66 | redisClusterClient.setOptions(clientOptions.build()); 67 | 68 | final RedisCodec codec = redisCodec.build(); 69 | 70 | final StatefulRedisClusterConnection connection = redisClusterClient.connect(codec); 71 | 72 | // manage client and connection 73 | lifecycle.manage(new RedisClientManager(redisClusterClient, connection, name)); 74 | 75 | // health check 76 | healthChecks.register(name, new RedisHealthCheck(() -> connection.sync().ping())); 77 | 78 | // metrics (latency and other connection events) integration 79 | final List eventVisitors = buildEventVisitors(metrics); 80 | redisClusterClient.getResources() 81 | .eventBus() 82 | .get() 83 | .subscribe(new LettuceMetricsSubscriber(buildEventVisitors(metrics))); 84 | 85 | return connection; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/clientoptions/ClientOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.clientoptions; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.dropwizard.redis.socket.SocketOptionsFactory; 5 | import io.dropwizard.redis.ssl.SslOptionsFactory; 6 | import io.dropwizard.redis.timeout.TimeoutOptionsFactory; 7 | import io.lettuce.core.ClientOptions; 8 | 9 | import jakarta.validation.Valid; 10 | import jakarta.validation.constraints.Min; 11 | import jakarta.validation.constraints.NotNull; 12 | 13 | public class ClientOptionsFactory { 14 | @JsonProperty 15 | protected boolean pingBeforeActivateConnection = ClientOptions.DEFAULT_PING_BEFORE_ACTIVATE_CONNECTION; 16 | 17 | @JsonProperty 18 | protected boolean cancelCommandsOnReconnectFailure = ClientOptions.DEFAULT_CANCEL_CMD_RECONNECT_FAIL; 19 | 20 | @JsonProperty 21 | protected boolean publishOnScheduler = ClientOptions.DEFAULT_PUBLISH_ON_SCHEDULER; 22 | 23 | @JsonProperty 24 | protected boolean autoReconnect = ClientOptions.DEFAULT_AUTO_RECONNECT; 25 | 26 | @JsonProperty 27 | protected boolean suspendReconnectOnProtocolFailure = ClientOptions.DEFAULT_SUSPEND_RECONNECT_PROTO_FAIL; 28 | 29 | @Min(0) 30 | @JsonProperty 31 | protected int requestQueueSize = ClientOptions.DEFAULT_REQUEST_QUEUE_SIZE; 32 | 33 | @NotNull 34 | @JsonProperty 35 | protected ClientOptions.DisconnectedBehavior disconnectedBehavior = ClientOptions.DEFAULT_DISCONNECTED_BEHAVIOR; 36 | 37 | @Valid 38 | @NotNull 39 | @JsonProperty 40 | protected SocketOptionsFactory socketOptions = new SocketOptionsFactory(); 41 | 42 | @Valid 43 | @JsonProperty 44 | protected SslOptionsFactory sslOptions; 45 | 46 | @Valid 47 | @NotNull 48 | @JsonProperty 49 | protected TimeoutOptionsFactory timeoutOptions = new TimeoutOptionsFactory(); 50 | 51 | public boolean isPingBeforeActivateConnection() { 52 | return pingBeforeActivateConnection; 53 | } 54 | 55 | public void setPingBeforeActivateConnection(final boolean pingBeforeActivateConnection) { 56 | this.pingBeforeActivateConnection = pingBeforeActivateConnection; 57 | } 58 | 59 | public boolean isCancelCommandsOnReconnectFailure() { 60 | return cancelCommandsOnReconnectFailure; 61 | } 62 | 63 | public void setCancelCommandsOnReconnectFailure(final boolean cancelCommandsOnReconnectFailure) { 64 | this.cancelCommandsOnReconnectFailure = cancelCommandsOnReconnectFailure; 65 | } 66 | 67 | public boolean isPublishOnScheduler() { 68 | return publishOnScheduler; 69 | } 70 | 71 | public void setPublishOnScheduler(final boolean publishOnScheduler) { 72 | this.publishOnScheduler = publishOnScheduler; 73 | } 74 | 75 | public boolean isAutoReconnect() { 76 | return autoReconnect; 77 | } 78 | 79 | public void setAutoReconnect(final boolean autoReconnect) { 80 | this.autoReconnect = autoReconnect; 81 | } 82 | 83 | public boolean isSuspendReconnectOnProtocolFailure() { 84 | return suspendReconnectOnProtocolFailure; 85 | } 86 | 87 | public void setSuspendReconnectOnProtocolFailure(final boolean suspendReconnectOnProtocolFailure) { 88 | this.suspendReconnectOnProtocolFailure = suspendReconnectOnProtocolFailure; 89 | } 90 | 91 | public int getRequestQueueSize() { 92 | return requestQueueSize; 93 | } 94 | 95 | public void setRequestQueueSize(final int requestQueueSize) { 96 | this.requestQueueSize = requestQueueSize; 97 | } 98 | 99 | public ClientOptions.DisconnectedBehavior getDisconnectedBehavior() { 100 | return disconnectedBehavior; 101 | } 102 | 103 | public void setDisconnectedBehavior(final ClientOptions.DisconnectedBehavior disconnectedBehavior) { 104 | this.disconnectedBehavior = disconnectedBehavior; 105 | } 106 | 107 | public SocketOptionsFactory getSocketOptions() { 108 | return socketOptions; 109 | } 110 | 111 | public void setSocketOptions(final SocketOptionsFactory socketOptions) { 112 | this.socketOptions = socketOptions; 113 | } 114 | 115 | public SslOptionsFactory getSslOptions() { 116 | return sslOptions; 117 | } 118 | 119 | public void setSslOptions(final SslOptionsFactory sslOptions) { 120 | this.sslOptions = sslOptions; 121 | } 122 | 123 | public TimeoutOptionsFactory getTimeoutOptions() { 124 | return timeoutOptions; 125 | } 126 | 127 | public void setTimeoutOptions(final TimeoutOptionsFactory timeoutOptions) { 128 | this.timeoutOptions = timeoutOptions; 129 | } 130 | 131 | public ClientOptions build() { 132 | final ClientOptions.Builder builder = ClientOptions.builder(); 133 | 134 | return addBuildParams(builder).build(); 135 | } 136 | 137 | protected ClientOptions.Builder addBuildParams(final ClientOptions.Builder builder) { 138 | builder.pingBeforeActivateConnection(pingBeforeActivateConnection) 139 | .cancelCommandsOnReconnectFailure(cancelCommandsOnReconnectFailure) 140 | .publishOnScheduler(publishOnScheduler) 141 | .autoReconnect(autoReconnect) 142 | .suspendReconnectOnProtocolFailure(suspendReconnectOnProtocolFailure) 143 | .requestQueueSize(requestQueueSize) 144 | .disconnectedBehavior(disconnectedBehavior) 145 | .socketOptions(socketOptions.build()) 146 | .timeoutOptions(timeoutOptions.build()); 147 | 148 | if (sslOptions != null && sslOptions.isEnabled()) { 149 | builder.sslOptions(sslOptions.build()); 150 | } 151 | 152 | return builder; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/clientoptions/ClusterClientOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.clientoptions; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.dropwizard.redis.topology.ClusterTopologyRefreshOptionsFactory; 5 | import io.lettuce.core.cluster.ClusterClientOptions; 6 | import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.Min; 10 | 11 | public class ClusterClientOptionsFactory extends ClientOptionsFactory { 12 | @JsonProperty 13 | private boolean validateClusterNodeMembership = ClusterClientOptions.DEFAULT_VALIDATE_CLUSTER_MEMBERSHIP; 14 | 15 | @Min(0) 16 | @JsonProperty 17 | private int maxRedirects = ClusterClientOptions.DEFAULT_MAX_REDIRECTS; 18 | 19 | @Valid 20 | @JsonProperty 21 | private ClusterTopologyRefreshOptionsFactory topologyRefreshOptions; 22 | 23 | public boolean isValidateClusterNodeMembership() { 24 | return validateClusterNodeMembership; 25 | } 26 | 27 | public void setValidateClusterNodeMembership(final boolean validateClusterNodeMembership) { 28 | this.validateClusterNodeMembership = validateClusterNodeMembership; 29 | } 30 | 31 | public int getMaxRedirects() { 32 | return maxRedirects; 33 | } 34 | 35 | public void setMaxRedirects(final int maxRedirects) { 36 | this.maxRedirects = maxRedirects; 37 | } 38 | 39 | public ClusterTopologyRefreshOptionsFactory getTopologyRefreshOptions() { 40 | return topologyRefreshOptions; 41 | } 42 | 43 | public void setTopologyRefreshOptions(final ClusterTopologyRefreshOptionsFactory topologyRefreshOptions) { 44 | this.topologyRefreshOptions = topologyRefreshOptions; 45 | } 46 | 47 | @Override 48 | public ClusterClientOptions build() { 49 | final ClusterClientOptions.Builder builder = ((ClusterClientOptions.Builder) super.addBuildParams(ClusterClientOptions.builder())) 50 | .validateClusterNodeMembership(validateClusterNodeMembership) 51 | .maxRedirects(maxRedirects); 52 | 53 | if (topologyRefreshOptions != null) { 54 | final ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = topologyRefreshOptions.build(); 55 | builder.topologyRefreshOptions(clusterTopologyRefreshOptions); 56 | } 57 | 58 | return builder.build(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/clientresources/ClientResourcesFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.clientresources; 2 | 3 | import brave.Tracing; 4 | import com.codahale.metrics.MetricRegistry; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 7 | import io.dropwizard.jackson.Discoverable; 8 | import io.dropwizard.redis.delay.DelayFactory; 9 | import io.dropwizard.redis.delay.ExponentialDelayFactory; 10 | import io.dropwizard.redis.event.DefaultEventBusFactory; 11 | import io.dropwizard.redis.event.DefaultEventLoopGroupProviderFactory; 12 | import io.dropwizard.redis.event.EventBusFactory; 13 | import io.dropwizard.redis.event.EventLoopGroupProviderFactory; 14 | import io.dropwizard.redis.metrics.CommandLatencyRecorderFactory; 15 | import io.dropwizard.redis.metrics.DefaultCommandLatencyCollectorFactory; 16 | import io.dropwizard.redis.metrics.DefaultEventPublisherOptionsFactory; 17 | import io.dropwizard.redis.metrics.EventPublisherOptionsFactory; 18 | import io.dropwizard.redis.netty.DefaultEventExecutorGroupFactory; 19 | import io.dropwizard.redis.netty.EventExecutorGroupFactory; 20 | import io.lettuce.core.resource.ClientResources; 21 | import io.lettuce.core.resource.DefaultClientResources; 22 | import org.checkerframework.checker.nullness.qual.Nullable; 23 | 24 | import jakarta.validation.Valid; 25 | import jakarta.validation.constraints.Min; 26 | import jakarta.validation.constraints.NotNull; 27 | 28 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 29 | public abstract class ClientResourcesFactory implements Discoverable { 30 | @Valid 31 | @NotNull 32 | @JsonProperty 33 | protected CommandLatencyRecorderFactory commandLatencyRecorder = new DefaultCommandLatencyCollectorFactory(); 34 | 35 | @Valid 36 | @NotNull 37 | @JsonProperty 38 | protected EventPublisherOptionsFactory eventPublisherOptions = new DefaultEventPublisherOptionsFactory(); 39 | 40 | @Valid 41 | @NotNull 42 | @JsonProperty 43 | protected EventBusFactory eventBusFactory = new DefaultEventBusFactory(); 44 | 45 | @Valid 46 | @NotNull 47 | @JsonProperty 48 | protected EventExecutorGroupFactory eventExecutorGroup = new DefaultEventExecutorGroupFactory(); 49 | 50 | @Valid 51 | @NotNull 52 | @JsonProperty 53 | protected EventLoopGroupProviderFactory eventLoopGroupProvider = new DefaultEventLoopGroupProviderFactory(); 54 | 55 | @Min(1) 56 | @JsonProperty 57 | protected int computationThreads = DefaultClientResources.DEFAULT_COMPUTATION_THREADS; 58 | 59 | @Min(1) 60 | @JsonProperty 61 | protected int ioThreads = DefaultClientResources.DEFAULT_IO_THREADS; 62 | 63 | @Valid 64 | @NotNull 65 | protected DelayFactory delay = new ExponentialDelayFactory(); 66 | 67 | public CommandLatencyRecorderFactory getCommandLatencyRecorder() { 68 | return commandLatencyRecorder; 69 | } 70 | 71 | public void setCommandLatencyRecorder(final CommandLatencyRecorderFactory commandLatencyRecorder) { 72 | this.commandLatencyRecorder = commandLatencyRecorder; 73 | } 74 | 75 | public EventPublisherOptionsFactory getEventPublisherOptions() { 76 | return eventPublisherOptions; 77 | } 78 | 79 | public void setEventPublisherOptions(final EventPublisherOptionsFactory eventPublisherOptions) { 80 | this.eventPublisherOptions = eventPublisherOptions; 81 | } 82 | 83 | public EventBusFactory getEventBusFactory() { 84 | return eventBusFactory; 85 | } 86 | 87 | public void setEventBusFactory(final EventBusFactory eventBusFactory) { 88 | this.eventBusFactory = eventBusFactory; 89 | } 90 | 91 | public EventExecutorGroupFactory getEventExecutorGroup() { 92 | return eventExecutorGroup; 93 | } 94 | 95 | public void setEventExecutorGroup(final EventExecutorGroupFactory eventExecutorGroup) { 96 | this.eventExecutorGroup = eventExecutorGroup; 97 | } 98 | 99 | public EventLoopGroupProviderFactory getEventLoopGroupProvider() { 100 | return eventLoopGroupProvider; 101 | } 102 | 103 | public void setEventLoopGroupProvider(final EventLoopGroupProviderFactory eventLoopGroupProvider) { 104 | this.eventLoopGroupProvider = eventLoopGroupProvider; 105 | } 106 | 107 | public int getComputationThreads() { 108 | return computationThreads; 109 | } 110 | 111 | public void setComputationThreads(final int computationThreads) { 112 | this.computationThreads = computationThreads; 113 | } 114 | 115 | public int getIoThreads() { 116 | return ioThreads; 117 | } 118 | 119 | public void setIoThreads(final int ioThreads) { 120 | this.ioThreads = ioThreads; 121 | } 122 | 123 | public DelayFactory getDelay() { 124 | return delay; 125 | } 126 | 127 | public void setDelay(final DelayFactory delay) { 128 | this.delay = delay; 129 | } 130 | 131 | public abstract ClientResources build(final String name, final MetricRegistry metrics, @Nullable final Tracing tracing); 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/clientresources/DefaultClientResourcesFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.clientresources; 2 | 3 | import brave.Tracing; 4 | import com.codahale.metrics.MetricRegistry; 5 | import com.fasterxml.jackson.annotation.JsonTypeName; 6 | import io.lettuce.core.resource.ClientResources; 7 | import io.lettuce.core.resource.DefaultClientResources; 8 | import io.lettuce.core.tracing.BraveTracing; 9 | import io.netty.util.concurrent.EventExecutorGroup; 10 | import org.checkerframework.checker.nullness.qual.Nullable; 11 | import reactor.core.scheduler.Schedulers; 12 | 13 | @JsonTypeName("default") 14 | public class DefaultClientResourcesFactory extends ClientResourcesFactory { 15 | @Override 16 | public ClientResources build(final String name, final MetricRegistry metrics, @Nullable final Tracing tracing) { 17 | final EventExecutorGroup executorGroup = eventExecutorGroup.build(computationThreads, name, metrics); 18 | 19 | final ClientResources.Builder builder = DefaultClientResources.builder() 20 | .commandLatencyRecorder(commandLatencyRecorder.build(metrics)) 21 | .commandLatencyPublisherOptions(eventPublisherOptions.build()) 22 | .eventExecutorGroup(executorGroup) 23 | .eventBus(eventBusFactory.build(Schedulers.fromExecutor(executorGroup))) 24 | .computationThreadPoolSize(computationThreads) 25 | .ioThreadPoolSize(ioThreads) 26 | .eventLoopGroupProvider(eventLoopGroupProvider.build(computationThreads)) 27 | .reconnectDelay(delay.build()); 28 | 29 | if (tracing != null) { 30 | builder.tracing(BraveTracing.create(tracing)); 31 | } 32 | 33 | // TODO: add netty customizer 34 | // TODO: add dns resolver 35 | // TODO: add socket address resolver 36 | // TODO: add netty Timer 37 | 38 | return builder.build(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/codec/ByteArrayCodecFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.codec; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeName; 4 | import io.lettuce.core.codec.ByteArrayCodec; 5 | import io.lettuce.core.codec.RedisCodec; 6 | 7 | @JsonTypeName("byte-array") 8 | public class ByteArrayCodecFactory implements RedisCodecFactory { 9 | @Override 10 | public RedisCodec build() { 11 | return new ByteArrayCodec(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/codec/CompressionCodecFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.codec; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeName; 5 | import io.lettuce.core.codec.CompressionCodec; 6 | import io.lettuce.core.codec.RedisCodec; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotNull; 10 | 11 | @JsonTypeName("compression") 12 | public class CompressionCodecFactory implements RedisCodecFactory { 13 | @Valid 14 | @NotNull 15 | @JsonProperty 16 | private RedisCodecFactory delegatee; 17 | 18 | @NotNull 19 | @JsonProperty 20 | private CompressionCodec.CompressionType compressionType; 21 | 22 | public RedisCodecFactory getDelegatee() { 23 | return delegatee; 24 | } 25 | 26 | public void setDelegatee(final RedisCodecFactory delegatee) { 27 | this.delegatee = delegatee; 28 | } 29 | 30 | public CompressionCodec.CompressionType getCompressionType() { 31 | return compressionType; 32 | } 33 | 34 | public void setCompressionType(final CompressionCodec.CompressionType compressionType) { 35 | this.compressionType = compressionType; 36 | } 37 | 38 | @Override 39 | public RedisCodec build() { 40 | return CompressionCodec.valueCompressor(delegatee.build(), compressionType); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/codec/RedisCodecFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.codec; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import io.dropwizard.jackson.Discoverable; 5 | import io.lettuce.core.codec.RedisCodec; 6 | 7 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 8 | public interface RedisCodecFactory extends Discoverable { 9 | RedisCodec build(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/codec/StringCodecFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.codec; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeName; 4 | import io.lettuce.core.codec.RedisCodec; 5 | import io.lettuce.core.codec.StringCodec; 6 | 7 | @JsonTypeName("string") 8 | public class StringCodecFactory implements RedisCodecFactory { 9 | @Override 10 | public RedisCodec build() { 11 | return new StringCodec(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/codec/Utf8StringCodecFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.codec; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeName; 4 | import io.lettuce.core.codec.RedisCodec; 5 | import io.lettuce.core.codec.Utf8StringCodec; 6 | 7 | @JsonTypeName("utf8-string") 8 | public class Utf8StringCodecFactory implements RedisCodecFactory { 9 | @Override 10 | public RedisCodec build() { 11 | return new Utf8StringCodec(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/delay/ConstantDelayFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeName; 5 | import io.dropwizard.util.Duration; 6 | import io.lettuce.core.resource.Delay; 7 | 8 | import java.util.function.Supplier; 9 | 10 | import jakarta.validation.constraints.NotNull; 11 | 12 | @JsonTypeName("constant") 13 | public class ConstantDelayFactory implements DelayFactory { 14 | @NotNull 15 | @JsonProperty 16 | private Duration duration; 17 | 18 | public Duration getDuration() { 19 | return duration; 20 | } 21 | 22 | public void setDuration(final Duration duration) { 23 | this.duration = duration; 24 | } 25 | 26 | @Override 27 | public Supplier build() { 28 | final Delay delay = Delay.constant(java.time.Duration.ofMillis(duration.toMilliseconds())); 29 | return () -> delay; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/delay/DecorrelatedJitterDelayFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeName; 5 | import io.dropwizard.util.Duration; 6 | import io.lettuce.core.resource.Delay; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.function.Supplier; 10 | 11 | import jakarta.validation.constraints.Min; 12 | import jakarta.validation.constraints.NotNull; 13 | 14 | @JsonTypeName("decorrelated-jitter") 15 | public class DecorrelatedJitterDelayFactory implements DelayFactory { 16 | @NotNull 17 | @JsonProperty 18 | private Duration lowerBound = Duration.seconds(0); 19 | 20 | @NotNull 21 | @JsonProperty 22 | private Duration upperBound = Duration.seconds(30); 23 | 24 | @Min(0L) 25 | @JsonProperty 26 | private long base = 0L; 27 | 28 | public Duration getLowerBound() { 29 | return lowerBound; 30 | } 31 | 32 | public void setLowerBound(final Duration lowerBound) { 33 | this.lowerBound = lowerBound; 34 | } 35 | 36 | public Duration getUpperBound() { 37 | return upperBound; 38 | } 39 | 40 | public void setUpperBound(final Duration upperBound) { 41 | this.upperBound = upperBound; 42 | } 43 | 44 | public long getBase() { 45 | return base; 46 | } 47 | 48 | public void setBase(final long base) { 49 | this.base = base; 50 | } 51 | 52 | @Override 53 | public Supplier build() { 54 | return Delay.decorrelatedJitter(lowerBound.toMilliseconds(), upperBound.toMilliseconds(), base, TimeUnit.MILLISECONDS); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/delay/DelayFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import io.dropwizard.jackson.Discoverable; 5 | import io.lettuce.core.resource.Delay; 6 | 7 | import java.util.function.Supplier; 8 | 9 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 10 | public interface DelayFactory extends Discoverable { 11 | Supplier build(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/delay/EqualJitterDelayFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeName; 5 | import io.dropwizard.util.Duration; 6 | import io.lettuce.core.resource.Delay; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.function.Supplier; 10 | 11 | import jakarta.validation.constraints.Min; 12 | import jakarta.validation.constraints.NotNull; 13 | 14 | @JsonTypeName("equal-jitter") 15 | public class EqualJitterDelayFactory implements DelayFactory { 16 | @NotNull 17 | @JsonProperty 18 | private Duration lowerBound = Duration.seconds(0); 19 | 20 | @NotNull 21 | @JsonProperty 22 | private Duration upperBound = Duration.seconds(30); 23 | 24 | @Min(0L) 25 | @JsonProperty 26 | private long base = 0L; 27 | 28 | public Duration getLowerBound() { 29 | return lowerBound; 30 | } 31 | 32 | public void setLowerBound(final Duration lowerBound) { 33 | this.lowerBound = lowerBound; 34 | } 35 | 36 | public Duration getUpperBound() { 37 | return upperBound; 38 | } 39 | 40 | public void setUpperBound(final Duration upperBound) { 41 | this.upperBound = upperBound; 42 | } 43 | 44 | public long getBase() { 45 | return base; 46 | } 47 | 48 | public void setBase(final long base) { 49 | this.base = base; 50 | } 51 | 52 | @Override 53 | public Supplier build() { 54 | return () -> Delay.equalJitter(lowerBound.toMilliseconds(), upperBound.toMilliseconds(), base, TimeUnit.MILLISECONDS); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/delay/ExponentialDelayFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeName; 5 | import io.dropwizard.util.Duration; 6 | import io.lettuce.core.resource.Delay; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.function.Supplier; 10 | 11 | import jakarta.validation.constraints.Min; 12 | import jakarta.validation.constraints.NotNull; 13 | 14 | @JsonTypeName("exponential") 15 | public class ExponentialDelayFactory implements DelayFactory { 16 | @NotNull 17 | @JsonProperty 18 | private Duration lowerBound = Duration.seconds(0); 19 | 20 | @NotNull 21 | @JsonProperty 22 | private Duration upperBound = Duration.seconds(30); 23 | 24 | @Min(2) 25 | @JsonProperty 26 | private int powersOf = 2; 27 | 28 | public Duration getLowerBound() { 29 | return lowerBound; 30 | } 31 | 32 | public void setLowerBound(final Duration lowerBound) { 33 | this.lowerBound = lowerBound; 34 | } 35 | 36 | public Duration getUpperBound() { 37 | return upperBound; 38 | } 39 | 40 | public void setUpperBound(final Duration upperBound) { 41 | this.upperBound = upperBound; 42 | } 43 | 44 | public int getPowersOf() { 45 | return powersOf; 46 | } 47 | 48 | public void setPowersOf(final int powersOf) { 49 | this.powersOf = powersOf; 50 | } 51 | 52 | @Override 53 | public Supplier build() { 54 | return () -> Delay.exponential(java.time.Duration.ofMillis(lowerBound.toMilliseconds()), 55 | java.time.Duration.ofMillis(upperBound.toMilliseconds()), 56 | powersOf, 57 | TimeUnit.MILLISECONDS); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/delay/FullJitterDelayFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeName; 5 | import io.dropwizard.util.Duration; 6 | import io.lettuce.core.resource.Delay; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.function.Supplier; 10 | 11 | import jakarta.validation.constraints.Min; 12 | import jakarta.validation.constraints.NotNull; 13 | 14 | @JsonTypeName("full-jitter") 15 | public class FullJitterDelayFactory implements DelayFactory { 16 | @NotNull 17 | @JsonProperty 18 | private Duration lowerBound = Duration.seconds(0); 19 | 20 | @NotNull 21 | @JsonProperty 22 | private Duration upperBound = Duration.seconds(30); 23 | 24 | @Min(0L) 25 | @JsonProperty 26 | private long base = 0L; 27 | 28 | public Duration getLowerBound() { 29 | return lowerBound; 30 | } 31 | 32 | public void setLowerBound(final Duration lowerBound) { 33 | this.lowerBound = lowerBound; 34 | } 35 | 36 | public Duration getUpperBound() { 37 | return upperBound; 38 | } 39 | 40 | public void setUpperBound(final Duration upperBound) { 41 | this.upperBound = upperBound; 42 | } 43 | 44 | public long getBase() { 45 | return base; 46 | } 47 | 48 | public void setBase(final long base) { 49 | this.base = base; 50 | } 51 | 52 | @Override 53 | public Supplier build() { 54 | return () -> Delay.fullJitter(lowerBound.toMilliseconds(), upperBound.toMilliseconds(), base, TimeUnit.MILLISECONDS); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/event/DefaultEventBusFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.event; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeName; 4 | import io.lettuce.core.event.DefaultEventBus; 5 | import io.lettuce.core.event.EventBus; 6 | import reactor.core.scheduler.Scheduler; 7 | 8 | @JsonTypeName("default") 9 | public class DefaultEventBusFactory implements EventBusFactory { 10 | @Override 11 | public EventBus build(final Scheduler scheduler) { 12 | return new DefaultEventBus(scheduler); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/event/DefaultEventLoopGroupProviderFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.event; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeName; 4 | import io.lettuce.core.resource.DefaultEventLoopGroupProvider; 5 | import io.lettuce.core.resource.EventLoopGroupProvider; 6 | 7 | @JsonTypeName("default") 8 | public class DefaultEventLoopGroupProviderFactory implements EventLoopGroupProviderFactory { 9 | @Override 10 | public EventLoopGroupProvider build(int threads) { 11 | return new DefaultEventLoopGroupProvider(threads); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/event/EventBusFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.event; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import io.dropwizard.jackson.Discoverable; 5 | import io.lettuce.core.event.EventBus; 6 | import reactor.core.scheduler.Scheduler; 7 | 8 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 9 | public interface EventBusFactory extends Discoverable { 10 | EventBus build(final Scheduler scheduler); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/event/EventLoopGroupProviderFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.event; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import io.dropwizard.jackson.Discoverable; 5 | import io.lettuce.core.resource.EventLoopGroupProvider; 6 | 7 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 8 | public interface EventLoopGroupProviderFactory extends Discoverable { 9 | EventLoopGroupProvider build(int threads); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/health/Pingable.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.health; 2 | 3 | public interface Pingable { 4 | String ping(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/health/RedisHealthCheck.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.health; 2 | 3 | import com.codahale.metrics.health.HealthCheck; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * Performs health checks against a Redis cluster. 9 | * Health check pings the current master, determining whether it is healthy for reads/writes. 10 | */ 11 | public class RedisHealthCheck extends HealthCheck { 12 | private static final Logger log = LoggerFactory.getLogger(RedisHealthCheck.class); 13 | 14 | public static final String HEALTHY_STRING = "PONG"; 15 | 16 | private final Pingable client; 17 | 18 | public RedisHealthCheck(final Pingable client) { 19 | this.client = client; 20 | } 21 | 22 | public Pingable getClient() { 23 | return client; 24 | } 25 | 26 | @Override 27 | protected Result check() { 28 | try { 29 | return HEALTHY_STRING.equals(client.ping()) ? Result.healthy() : Result.unhealthy("Could not ping Redis"); 30 | } catch (final RuntimeException e) { 31 | log.warn("Redis health check failed due to exception", e); 32 | return Result.unhealthy(e); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/managed/RedisClientManager.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.managed; 2 | 3 | import io.dropwizard.lifecycle.Managed; 4 | import io.lettuce.core.AbstractRedisClient; 5 | import io.lettuce.core.api.StatefulConnection; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import static java.util.Objects.requireNonNull; 10 | 11 | /** 12 | * Manages the Redis client lifecycle alongside the application's lifecycle. 13 | */ 14 | public class RedisClientManager implements Managed { 15 | private static final Logger log = LoggerFactory.getLogger(RedisClientManager.class); 16 | 17 | private final AbstractRedisClient client; 18 | private final StatefulConnection connection; 19 | private final String name; 20 | 21 | public RedisClientManager(final AbstractRedisClient client, final StatefulConnection connection, final String name) { 22 | this.client = requireNonNull(client); 23 | this.connection = requireNonNull(connection); 24 | this.name = requireNonNull(name); 25 | } 26 | 27 | @Override 28 | public void start() throws Exception { 29 | log.info("redis={} starting", name); 30 | } 31 | 32 | @Override 33 | public void stop() throws Exception { 34 | connection.close(); 35 | client.shutdown(); 36 | log.info("redis={} shut down", name); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/CommandLatencyRecorderFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 5 | import io.dropwizard.jackson.Discoverable; 6 | import io.lettuce.core.metrics.CommandLatencyRecorder; 7 | 8 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 9 | public interface CommandLatencyRecorderFactory extends Discoverable { 10 | CommandLatencyRecorder build(MetricRegistry metricRegistry); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/DefaultCommandLatencyCollectorFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonTypeName; 6 | import io.lettuce.core.metrics.DefaultCommandLatencyCollector; 7 | import io.lettuce.core.metrics.DefaultCommandLatencyCollectorOptions; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import jakarta.validation.constraints.NotNull; 12 | import jakarta.validation.constraints.Size; 13 | 14 | @JsonTypeName("default") 15 | public class DefaultCommandLatencyCollectorFactory implements CommandLatencyRecorderFactory { 16 | @NotNull 17 | @JsonProperty 18 | private TimeUnit targetUnit = DefaultCommandLatencyCollectorOptions.DEFAULT_TARGET_UNIT; 19 | 20 | @NotNull 21 | @Size(min = 1) 22 | @JsonProperty 23 | private double[] targetPercentiles = DefaultCommandLatencyCollectorOptions.DEFAULT_TARGET_PERCENTILES; 24 | 25 | @JsonProperty 26 | private boolean resetLatenciesAfterEvent = DefaultCommandLatencyCollectorOptions.DEFAULT_RESET_LATENCIES_AFTER_EVENT; 27 | 28 | @JsonProperty 29 | private boolean localDistinction = DefaultCommandLatencyCollectorOptions.DEFAULT_LOCAL_DISTINCTION; 30 | 31 | @JsonProperty 32 | private boolean enabled = DefaultCommandLatencyCollectorOptions.DEFAULT_ENABLED; 33 | 34 | public TimeUnit getTargetUnit() { 35 | return targetUnit; 36 | } 37 | 38 | public void setTargetUnit(final TimeUnit targetUnit) { 39 | this.targetUnit = targetUnit; 40 | } 41 | 42 | public double[] getTargetPercentiles() { 43 | return targetPercentiles; 44 | } 45 | 46 | public void setTargetPercentiles(final double[] targetPercentiles) { 47 | this.targetPercentiles = targetPercentiles; 48 | } 49 | 50 | public boolean isResetLatenciesAfterEvent() { 51 | return resetLatenciesAfterEvent; 52 | } 53 | 54 | public void setResetLatenciesAfterEvent(final boolean resetLatenciesAfterEvent) { 55 | this.resetLatenciesAfterEvent = resetLatenciesAfterEvent; 56 | } 57 | 58 | public boolean isLocalDistinction() { 59 | return localDistinction; 60 | } 61 | 62 | public void setLocalDistinction(final boolean localDistinction) { 63 | this.localDistinction = localDistinction; 64 | } 65 | 66 | public boolean isEnabled() { 67 | return enabled; 68 | } 69 | 70 | public void setEnabled(final boolean enabled) { 71 | this.enabled = enabled; 72 | } 73 | 74 | @Override 75 | public DefaultCommandLatencyCollector build(MetricRegistry metricRegistry) { 76 | final DefaultCommandLatencyCollectorOptions.Builder builder = DefaultCommandLatencyCollectorOptions.builder() 77 | .targetUnit(targetUnit) 78 | .targetPercentiles(targetPercentiles) 79 | .resetLatenciesAfterEvent(resetLatenciesAfterEvent) 80 | .localDistinction(localDistinction); 81 | 82 | if (enabled) { 83 | builder.enable(); 84 | } else { 85 | builder.disable(); 86 | } 87 | 88 | return new DefaultCommandLatencyCollector(builder.build()); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/DefaultEventPublisherOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeName; 5 | import io.dropwizard.util.Duration; 6 | import io.lettuce.core.event.DefaultEventPublisherOptions; 7 | import io.lettuce.core.event.EventPublisherOptions; 8 | 9 | import jakarta.validation.constraints.NotNull; 10 | 11 | @JsonTypeName("default") 12 | public class DefaultEventPublisherOptionsFactory implements EventPublisherOptionsFactory { 13 | @NotNull 14 | @JsonProperty 15 | private Duration eventEmitInterval = Duration.minutes(DefaultEventPublisherOptions.DEFAULT_EMIT_INTERVAL); 16 | 17 | public Duration getEventEmitInterval() { 18 | return eventEmitInterval; 19 | } 20 | 21 | public void setEventEmitInterval(final Duration eventEmitInterval) { 22 | this.eventEmitInterval = eventEmitInterval; 23 | } 24 | 25 | @Override 26 | public EventPublisherOptions build() { 27 | return DefaultEventPublisherOptions.builder() 28 | .eventEmitInterval(java.time.Duration.ofSeconds(eventEmitInterval.toSeconds())) 29 | .build(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/DropwizardCommandLatencyRecorder.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import com.codahale.metrics.Timer; 5 | import io.lettuce.core.metrics.CommandLatencyId; 6 | import io.lettuce.core.metrics.CommandLatencyRecorder; 7 | import java.net.SocketAddress; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | import java.util.concurrent.TimeUnit; 11 | import io.lettuce.core.internal.LettuceAssert; 12 | import io.lettuce.core.protocol.ProtocolKeyword; 13 | import io.netty.channel.local.LocalAddress; 14 | 15 | import static com.codahale.metrics.MetricRegistry.name; 16 | 17 | public class DropwizardCommandLatencyRecorder implements CommandLatencyRecorder { 18 | 19 | static final String METRIC_COMPLETION = "lettuce.command.completion"; 20 | 21 | static final String METRIC_FIRST_RESPONSE = "lettuce.command.firstresponse"; 22 | 23 | private final MetricRegistry metricRegistry; 24 | 25 | private final boolean isEnabled; 26 | 27 | private final Map completionTimers = new ConcurrentHashMap<>(); 28 | 29 | private final Map firstResponseTimers = new ConcurrentHashMap<>(); 30 | 31 | 32 | public DropwizardCommandLatencyRecorder(MetricRegistry metricRegistry, boolean isEnabled) { 33 | LettuceAssert.notNull(metricRegistry, "MetricRegistry must not be null"); 34 | 35 | this.metricRegistry = metricRegistry; 36 | this.isEnabled = isEnabled; 37 | } 38 | 39 | @Override 40 | public void recordCommandLatency(SocketAddress local, SocketAddress remote, ProtocolKeyword protocolKeyword, 41 | long firstResponseLatency, long completionLatency) { 42 | if (!isEnabled()) { 43 | return; 44 | } 45 | 46 | CommandLatencyId commandLatencyId = CommandLatencyId.create(local, remote, protocolKeyword); 47 | 48 | Timer firstResponseTimer = firstResponseTimers.computeIfAbsent(commandLatencyId, c -> metricRegistry 49 | .timer(name(METRIC_FIRST_RESPONSE, c.commandType().toString()))); 50 | firstResponseTimer.update(firstResponseLatency, TimeUnit.NANOSECONDS); 51 | 52 | Timer completionTimer = completionTimers.computeIfAbsent(commandLatencyId, c -> metricRegistry 53 | .timer(name(METRIC_COMPLETION, c.commandType().toString()))); 54 | completionTimer.update(completionLatency, TimeUnit.NANOSECONDS); 55 | } 56 | 57 | @Override 58 | public boolean isEnabled() { 59 | return isEnabled; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/DropwizardCommandLatencyRecorderFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonTypeName; 6 | import io.lettuce.core.metrics.CommandLatencyRecorder; 7 | import io.lettuce.core.metrics.DefaultCommandLatencyCollectorOptions; 8 | 9 | @JsonTypeName("dropwizard") 10 | public class DropwizardCommandLatencyRecorderFactory implements CommandLatencyRecorderFactory { 11 | 12 | @JsonProperty 13 | private boolean enabled = true; 14 | 15 | public boolean isEnabled() { 16 | return enabled; 17 | } 18 | 19 | public void setEnabled(boolean enabled) { 20 | this.enabled = enabled; 21 | } 22 | 23 | @Override 24 | public CommandLatencyRecorder build(MetricRegistry metricRegistry) { 25 | return new DropwizardCommandLatencyRecorder(metricRegistry, enabled); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/EventPublisherOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import io.dropwizard.jackson.Discoverable; 5 | import io.lettuce.core.event.EventPublisherOptions; 6 | 7 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 8 | public interface EventPublisherOptionsFactory extends Discoverable { 9 | EventPublisherOptions build(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/LettuceMetricsSubscriber.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event; 2 | 3 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 4 | import io.dropwizard.redis.metrics.event.wrapper.EventWrapperFactory; 5 | import io.dropwizard.redis.metrics.event.wrapper.VisitableEventWrapper; 6 | import io.lettuce.core.event.Event; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | import java.util.function.Consumer; 11 | 12 | public class LettuceMetricsSubscriber implements Consumer { 13 | private final List eventVisitors; 14 | 15 | public LettuceMetricsSubscriber(List eventVisitors) { 16 | this.eventVisitors = eventVisitors; 17 | } 18 | 19 | @Override 20 | public void accept(Event event) { 21 | final Optional eventWrapperOpt = EventWrapperFactory.build(event); 22 | eventWrapperOpt.ifPresent(eventWrapper -> eventVisitors.forEach(eventWrapper::accept)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/visitor/ClusterTopologyChangedEventVisitor.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.visitor; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 5 | import io.lettuce.core.event.connection.ConnectedEvent; 6 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 7 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 8 | import io.lettuce.core.event.connection.DisconnectedEvent; 9 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import static java.util.Objects.requireNonNull; 14 | 15 | public class ClusterTopologyChangedEventVisitor implements EventVisitor { 16 | private static final Logger log = LoggerFactory.getLogger(ClusterTopologyChangedEventVisitor.class); 17 | 18 | private final String name; 19 | private final MetricRegistry metrics; 20 | 21 | public ClusterTopologyChangedEventVisitor(final String name, final MetricRegistry metrics) { 22 | this.name = MetricRegistry.name(requireNonNull(name), "event.cluster-topology-change"); 23 | this.metrics = requireNonNull(metrics); 24 | } 25 | 26 | @Override 27 | public void visit(final CommandLatencyEvent event) { 28 | // do nothing 29 | } 30 | 31 | @Override 32 | public void visit(final DisconnectedEvent event) { 33 | // do nothing 34 | } 35 | 36 | @Override 37 | public void visit(final ClusterTopologyChangedEvent event) { 38 | metrics.counter(name).inc(); 39 | log.debug("Cluster topology change event occurred {}", event); 40 | } 41 | 42 | @Override 43 | public void visit(final ConnectedEvent event) { 44 | // do nothing 45 | } 46 | 47 | @Override 48 | public void visit(final ConnectionActivatedEvent event) { 49 | // do nothing 50 | } 51 | 52 | @Override 53 | public void visit(final ConnectionDeactivatedEvent event) { 54 | // do nothing 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/visitor/CommandLatencyEventVisitor.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.visitor; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 5 | import io.lettuce.core.event.connection.ConnectedEvent; 6 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 7 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 8 | import io.lettuce.core.event.connection.DisconnectedEvent; 9 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | import static java.util.Objects.requireNonNull; 15 | 16 | public class CommandLatencyEventVisitor implements EventVisitor { 17 | private final String name; 18 | private final MetricRegistry metrics; 19 | private final Map metricNames; 20 | 21 | public CommandLatencyEventVisitor(final String name, final MetricRegistry metrics) { 22 | this(name, metrics, new HashMap<>()); 23 | } 24 | 25 | // Visible for testing 26 | CommandLatencyEventVisitor(final String name, final MetricRegistry metrics, final Map metricNames) { 27 | this.name = MetricRegistry.name(requireNonNull(name), "event.latency"); 28 | this.metrics = requireNonNull(metrics); 29 | this.metricNames = requireNonNull(metricNames); 30 | } 31 | 32 | @Override 33 | public void visit(final CommandLatencyEvent event) { 34 | event.getLatencies().forEach((commandLatencyId, commandMetrics) -> { 35 | final String commandType = commandLatencyId.commandType().toString(); 36 | final String metricName = metricNames.computeIfAbsent(commandType, (type) -> MetricRegistry.name(name, type)); 37 | metrics.timer(metricName).update(commandMetrics.getCompletion().getMax(), commandMetrics.getTimeUnit()); 38 | }); 39 | } 40 | 41 | @Override 42 | public void visit(final DisconnectedEvent event) { 43 | // do nothing 44 | } 45 | 46 | @Override 47 | public void visit(final ClusterTopologyChangedEvent event) { 48 | // do nothing 49 | } 50 | 51 | @Override 52 | public void visit(final ConnectedEvent event) { 53 | // do nothing 54 | } 55 | 56 | @Override 57 | public void visit(final ConnectionActivatedEvent event) { 58 | // do nothing 59 | } 60 | 61 | @Override 62 | public void visit(final ConnectionDeactivatedEvent event) { 63 | // do nothing 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/visitor/ConnectedEventVisitor.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.visitor; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 5 | import io.lettuce.core.event.connection.ConnectedEvent; 6 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 7 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 8 | import io.lettuce.core.event.connection.DisconnectedEvent; 9 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import static java.util.Objects.requireNonNull; 14 | 15 | public class ConnectedEventVisitor implements EventVisitor { 16 | private static final Logger log = LoggerFactory.getLogger(ConnectedEventVisitor.class); 17 | 18 | private final String name; 19 | private final MetricRegistry metrics; 20 | 21 | // Visible for testing 22 | public ConnectedEventVisitor(final String name, final MetricRegistry metrics) { 23 | this.name = MetricRegistry.name(requireNonNull(name), "event.connected"); 24 | this.metrics = requireNonNull(metrics); 25 | } 26 | 27 | @Override 28 | public void visit(final CommandLatencyEvent event) { 29 | // do nothing 30 | } 31 | 32 | @Override 33 | public void visit(final DisconnectedEvent event) { 34 | // do nothing 35 | } 36 | 37 | @Override 38 | public void visit(final ClusterTopologyChangedEvent event) { 39 | // do nothing 40 | } 41 | 42 | @Override 43 | public void visit(final ConnectedEvent event) { 44 | metrics.counter(name).inc(); 45 | log.debug("Connected event occurred: {}", event); 46 | } 47 | 48 | @Override 49 | public void visit(final ConnectionActivatedEvent event) { 50 | // do nothing 51 | } 52 | 53 | @Override 54 | public void visit(final ConnectionDeactivatedEvent event) { 55 | // do nothing 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/visitor/ConnectionActivatedEventVisitor.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.visitor; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 5 | import io.lettuce.core.event.connection.ConnectedEvent; 6 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 7 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 8 | import io.lettuce.core.event.connection.DisconnectedEvent; 9 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import static java.util.Objects.requireNonNull; 14 | 15 | public class ConnectionActivatedEventVisitor implements EventVisitor { 16 | private static final Logger log = LoggerFactory.getLogger(ConnectionActivatedEventVisitor.class); 17 | 18 | private final String name; 19 | private final MetricRegistry metrics; 20 | 21 | // Visible for testing 22 | public ConnectionActivatedEventVisitor(final String name, final MetricRegistry metrics) { 23 | this.name = MetricRegistry.name(requireNonNull(name), "event.connection-activated"); 24 | this.metrics = requireNonNull(metrics); 25 | } 26 | 27 | @Override 28 | public void visit(final CommandLatencyEvent event) { 29 | // do nothing 30 | } 31 | 32 | @Override 33 | public void visit(final DisconnectedEvent event) { 34 | // do nothing 35 | } 36 | 37 | @Override 38 | public void visit(final ClusterTopologyChangedEvent event) { 39 | // do nothing 40 | } 41 | 42 | @Override 43 | public void visit(final ConnectedEvent event) { 44 | // do nothing 45 | } 46 | 47 | @Override 48 | public void visit(final ConnectionActivatedEvent event) { 49 | metrics.counter(name).inc(); 50 | log.debug("Connection activation event occurred: {}", event); 51 | } 52 | 53 | @Override 54 | public void visit(final ConnectionDeactivatedEvent event) { 55 | // do nothing 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/visitor/ConnectionDeactivatedEventVisitor.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.visitor; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 5 | import io.lettuce.core.event.connection.ConnectedEvent; 6 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 7 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 8 | import io.lettuce.core.event.connection.DisconnectedEvent; 9 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import static java.util.Objects.requireNonNull; 14 | 15 | public class ConnectionDeactivatedEventVisitor implements EventVisitor { 16 | private static final Logger log = LoggerFactory.getLogger(ConnectionDeactivatedEventVisitor.class); 17 | 18 | private final String name; 19 | private final MetricRegistry metrics; 20 | 21 | // Visible for testing 22 | public ConnectionDeactivatedEventVisitor(final String name, final MetricRegistry metrics) { 23 | this.name = MetricRegistry.name(requireNonNull(name), "event.connection-deactivated"); 24 | this.metrics = requireNonNull(metrics); 25 | } 26 | 27 | @Override 28 | public void visit(final CommandLatencyEvent event) { 29 | // do nothing 30 | } 31 | 32 | @Override 33 | public void visit(final DisconnectedEvent event) { 34 | // do nothing 35 | } 36 | 37 | @Override 38 | public void visit(final ClusterTopologyChangedEvent event) { 39 | // do nothing 40 | } 41 | 42 | @Override 43 | public void visit(final ConnectedEvent event) { 44 | // do nothing 45 | } 46 | 47 | @Override 48 | public void visit(final ConnectionActivatedEvent event) { 49 | // do nothing 50 | } 51 | 52 | @Override 53 | public void visit(final ConnectionDeactivatedEvent event) { 54 | metrics.counter(name).inc(); 55 | log.debug("Connection deactivation event occurred: {}", event); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/visitor/DisconnectedEventVisitor.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.visitor; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 5 | import io.lettuce.core.event.connection.ConnectedEvent; 6 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 7 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 8 | import io.lettuce.core.event.connection.DisconnectedEvent; 9 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import static java.util.Objects.requireNonNull; 14 | 15 | public class DisconnectedEventVisitor implements EventVisitor { 16 | private static final Logger log = LoggerFactory.getLogger(DisconnectedEventVisitor.class); 17 | 18 | private final String name; 19 | private final MetricRegistry metrics; 20 | 21 | // Visible for testing 22 | public DisconnectedEventVisitor(final String name, final MetricRegistry metrics) { 23 | this.name = MetricRegistry.name(requireNonNull(name), "event.disconnected"); 24 | this.metrics = requireNonNull(metrics); 25 | } 26 | 27 | @Override 28 | public void visit(final CommandLatencyEvent event) { 29 | // do nothing 30 | } 31 | 32 | @Override 33 | public void visit(final DisconnectedEvent event) { 34 | metrics.counter(name).inc(); 35 | log.debug("Disconnected event occurred {}", event); 36 | } 37 | 38 | @Override 39 | public void visit(final ClusterTopologyChangedEvent event) { 40 | // do nothing 41 | } 42 | 43 | @Override 44 | public void visit(final ConnectedEvent event) { 45 | // do nothing 46 | } 47 | 48 | @Override 49 | public void visit(final ConnectionActivatedEvent event) { 50 | // do nothing 51 | } 52 | 53 | @Override 54 | public void visit(final ConnectionDeactivatedEvent event) { 55 | // do nothing 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/visitor/EventVisitor.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.visitor; 2 | 3 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 4 | import io.lettuce.core.event.connection.ConnectedEvent; 5 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 6 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 7 | import io.lettuce.core.event.connection.DisconnectedEvent; 8 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 9 | 10 | public interface EventVisitor { 11 | void visit(CommandLatencyEvent event); 12 | 13 | void visit(DisconnectedEvent event); 14 | 15 | void visit(ClusterTopologyChangedEvent event); 16 | 17 | void visit(ConnectedEvent event); 18 | 19 | void visit(ConnectionActivatedEvent event); 20 | 21 | void visit(ConnectionDeactivatedEvent event); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/wrapper/EventWrapperFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.wrapper; 2 | 3 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 4 | import io.lettuce.core.event.Event; 5 | import io.lettuce.core.event.connection.ConnectedEvent; 6 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 7 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 8 | import io.lettuce.core.event.connection.DisconnectedEvent; 9 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 10 | 11 | import java.util.Optional; 12 | 13 | public class EventWrapperFactory { 14 | public static VisitableEventWrapper build(final CommandLatencyEvent event) { 15 | return new VisitableCommandLatencyEventWrapper(event); 16 | } 17 | 18 | public static VisitableEventWrapper build(final ClusterTopologyChangedEvent event) { 19 | return new VisitableClusterTopologyEventWrapper(event); 20 | } 21 | 22 | public static VisitableEventWrapper build(final ConnectedEvent event) { 23 | return new VisitableConnectedEventWrapper(event); 24 | } 25 | 26 | public static VisitableEventWrapper build(final ConnectionActivatedEvent event) { 27 | return new VisitableConnectionActivatedEventWrapper(event); 28 | } 29 | 30 | public static VisitableEventWrapper build(final ConnectionDeactivatedEvent event) { 31 | return new VisitableConnectionDeactivatedEventWrapper(event); 32 | } 33 | 34 | public static VisitableEventWrapper build(final DisconnectedEvent event) { 35 | return new VisitableDisconnectedEventWrapper(event); 36 | } 37 | 38 | public static Optional build(final Event event) { 39 | // TODO: any way to avoid needing to do these instanceof checks? 40 | if (event instanceof CommandLatencyEvent) { 41 | return Optional.of(build((CommandLatencyEvent) event)); 42 | } else if (event instanceof ClusterTopologyChangedEvent) { 43 | return Optional.of(build((ClusterTopologyChangedEvent) event)); 44 | } else if (event instanceof ConnectedEvent) { 45 | return Optional.of(build((ConnectedEvent) event)); 46 | } else if (event instanceof ConnectionActivatedEvent) { 47 | return Optional.of(build((ConnectionActivatedEvent) event)); 48 | } else if (event instanceof ConnectionDeactivatedEvent) { 49 | return Optional.of(build((ConnectionDeactivatedEvent) event)); 50 | } else if (event instanceof DisconnectedEvent) { 51 | return Optional.of(build((DisconnectedEvent) event)); 52 | } 53 | 54 | return Optional.empty(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/wrapper/VisitableClusterTopologyEventWrapper.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.wrapper; 2 | 3 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 4 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 5 | 6 | import static java.util.Objects.requireNonNull; 7 | 8 | public class VisitableClusterTopologyEventWrapper implements VisitableEventWrapper { 9 | private final ClusterTopologyChangedEvent event; 10 | 11 | public VisitableClusterTopologyEventWrapper(final ClusterTopologyChangedEvent event) { 12 | this.event = requireNonNull(event); 13 | } 14 | 15 | @Override 16 | public void accept(final EventVisitor visitor) { 17 | visitor.visit(event); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/wrapper/VisitableCommandLatencyEventWrapper.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.wrapper; 2 | 3 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 4 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 5 | 6 | import static java.util.Objects.requireNonNull; 7 | 8 | public class VisitableCommandLatencyEventWrapper implements VisitableEventWrapper { 9 | private final CommandLatencyEvent event; 10 | 11 | public VisitableCommandLatencyEventWrapper(final CommandLatencyEvent event) { 12 | this.event = requireNonNull(event); 13 | } 14 | 15 | @Override 16 | public void accept(final EventVisitor visitor) { 17 | visitor.visit(event); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/wrapper/VisitableConnectedEventWrapper.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.wrapper; 2 | 3 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 4 | import io.lettuce.core.event.connection.ConnectedEvent; 5 | 6 | import static java.util.Objects.requireNonNull; 7 | 8 | public class VisitableConnectedEventWrapper implements VisitableEventWrapper { 9 | private final ConnectedEvent event; 10 | 11 | public VisitableConnectedEventWrapper(final ConnectedEvent event) { 12 | this.event = requireNonNull(event); 13 | } 14 | 15 | @Override 16 | public void accept(final EventVisitor visitor) { 17 | visitor.visit(event); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/wrapper/VisitableConnectionActivatedEventWrapper.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.wrapper; 2 | 3 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 4 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 5 | 6 | import static java.util.Objects.requireNonNull; 7 | 8 | public class VisitableConnectionActivatedEventWrapper implements VisitableEventWrapper { 9 | private final ConnectionActivatedEvent event; 10 | 11 | public VisitableConnectionActivatedEventWrapper(final ConnectionActivatedEvent event) { 12 | this.event = requireNonNull(event); 13 | } 14 | 15 | @Override 16 | public void accept(final EventVisitor visitor) { 17 | visitor.visit(event); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/wrapper/VisitableConnectionDeactivatedEventWrapper.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.wrapper; 2 | 3 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 4 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 5 | 6 | import static java.util.Objects.requireNonNull; 7 | 8 | public class VisitableConnectionDeactivatedEventWrapper implements VisitableEventWrapper { 9 | private final ConnectionDeactivatedEvent event; 10 | 11 | public VisitableConnectionDeactivatedEventWrapper(final ConnectionDeactivatedEvent event) { 12 | this.event = requireNonNull(event); 13 | } 14 | 15 | @Override 16 | public void accept(final EventVisitor visitor) { 17 | visitor.visit(event); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/wrapper/VisitableDisconnectedEventWrapper.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.wrapper; 2 | 3 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 4 | import io.lettuce.core.event.connection.DisconnectedEvent; 5 | 6 | import static java.util.Objects.requireNonNull; 7 | 8 | public class VisitableDisconnectedEventWrapper implements VisitableEventWrapper { 9 | private final DisconnectedEvent event; 10 | 11 | public VisitableDisconnectedEventWrapper(final DisconnectedEvent event) { 12 | this.event = requireNonNull(event); 13 | } 14 | 15 | @Override 16 | public void accept(final EventVisitor visitor) { 17 | visitor.visit(event); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/metrics/event/wrapper/VisitableEventWrapper.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event.wrapper; 2 | 3 | import io.dropwizard.redis.metrics.event.visitor.EventVisitor; 4 | 5 | public interface VisitableEventWrapper { 6 | void accept(EventVisitor visitor); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/netty/DefaultEventExecutorGroupFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.netty; 2 | 3 | import com.codahale.metrics.InstrumentedThreadFactory; 4 | import com.codahale.metrics.MetricRegistry; 5 | import com.fasterxml.jackson.annotation.JsonTypeName; 6 | import io.netty.util.concurrent.DefaultEventExecutorGroup; 7 | import io.netty.util.concurrent.DefaultThreadFactory; 8 | import io.netty.util.concurrent.EventExecutorGroup; 9 | 10 | import java.util.concurrent.ThreadFactory; 11 | 12 | @JsonTypeName("default") 13 | public class DefaultEventExecutorGroupFactory implements EventExecutorGroupFactory { 14 | @Override 15 | public EventExecutorGroup build(final int threadPoolSize, final String name, final MetricRegistry metrics) { 16 | String poolName = String.format("%s-eventExecutorLoop", name); 17 | final ThreadFactory threadFactory = new DefaultThreadFactory(poolName, true); 18 | return new DefaultEventExecutorGroup(threadPoolSize, new InstrumentedThreadFactory(threadFactory, metrics, poolName)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/netty/EventExecutorGroupFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.netty; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 5 | import io.netty.util.concurrent.EventExecutorGroup; 6 | 7 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 8 | public interface EventExecutorGroupFactory { 9 | EventExecutorGroup build(final int threadPoolSize, final String name, final MetricRegistry metrics); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/socket/SocketOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.socket; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.dropwizard.util.Duration; 5 | import io.lettuce.core.SocketOptions; 6 | 7 | import jakarta.validation.constraints.NotNull; 8 | 9 | public class SocketOptionsFactory { 10 | @NotNull 11 | @JsonProperty 12 | private Duration connectTimeout = Duration.seconds(SocketOptions.DEFAULT_CONNECT_TIMEOUT); 13 | 14 | @JsonProperty 15 | private boolean keepAlive = SocketOptions.DEFAULT_SO_KEEPALIVE; 16 | 17 | @JsonProperty 18 | private boolean tcpNoDelay = SocketOptions.DEFAULT_SO_NO_DELAY; 19 | 20 | public Duration getConnectTimeout() { 21 | return connectTimeout; 22 | } 23 | 24 | public void setConnectTimeout(final Duration connectTimeout) { 25 | this.connectTimeout = connectTimeout; 26 | } 27 | 28 | public boolean isKeepAlive() { 29 | return keepAlive; 30 | } 31 | 32 | public void setKeepAlive(final boolean keepAlive) { 33 | this.keepAlive = keepAlive; 34 | } 35 | 36 | public boolean isTcpNoDelay() { 37 | return tcpNoDelay; 38 | } 39 | 40 | public void setTcpNoDelay(final boolean tcpNoDelay) { 41 | this.tcpNoDelay = tcpNoDelay; 42 | } 43 | 44 | public SocketOptions build() { 45 | return SocketOptions.builder() 46 | .connectTimeout(java.time.Duration.ofSeconds(connectTimeout.toSeconds())) 47 | .keepAlive(keepAlive) 48 | .tcpNoDelay(tcpNoDelay) 49 | .build(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/ssl/SslOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.ssl; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.lettuce.core.SslOptions; 5 | import io.netty.handler.ssl.SslProvider; 6 | 7 | import java.io.File; 8 | 9 | import jakarta.validation.constraints.NotNull; 10 | 11 | public class SslOptionsFactory { 12 | @JsonProperty 13 | private boolean enabled = true; 14 | 15 | @NotNull 16 | @JsonProperty 17 | private SslProvider sslProvider = SslOptions.DEFAULT_SSL_PROVIDER; 18 | 19 | @NotNull 20 | @JsonProperty 21 | private File keystore; 22 | 23 | @JsonProperty 24 | private String keystorePassword; 25 | 26 | @NotNull 27 | @JsonProperty 28 | private File truststore; 29 | 30 | @JsonProperty 31 | private String truststorePassword; 32 | 33 | public boolean isEnabled() { 34 | return enabled; 35 | } 36 | 37 | public void setEnabled(final boolean enabled) { 38 | this.enabled = enabled; 39 | } 40 | 41 | public SslProvider getSslProvider() { 42 | return sslProvider; 43 | } 44 | 45 | public void setSslProvider(final SslProvider sslProvider) { 46 | this.sslProvider = sslProvider; 47 | } 48 | 49 | public File getKeystore() { 50 | return keystore; 51 | } 52 | 53 | public void setKeystore(final File keystore) { 54 | this.keystore = keystore; 55 | } 56 | 57 | public String getKeystorePassword() { 58 | return keystorePassword; 59 | } 60 | 61 | public void setKeystorePassword(final String keystorePassword) { 62 | this.keystorePassword = keystorePassword; 63 | } 64 | 65 | public File getTruststore() { 66 | return truststore; 67 | } 68 | 69 | public void setTruststore(final File truststore) { 70 | this.truststore = truststore; 71 | } 72 | 73 | public String getTruststorePassword() { 74 | return truststorePassword; 75 | } 76 | 77 | public void setTruststorePassword(final String truststorePassword) { 78 | this.truststorePassword = truststorePassword; 79 | } 80 | 81 | public SslOptions build() { 82 | final SslOptions.Builder builder = SslOptions.builder(); 83 | 84 | switch (sslProvider) { 85 | case JDK: 86 | builder.jdkSslProvider(); 87 | break; 88 | case OPENSSL: 89 | case OPENSSL_REFCNT: 90 | builder.openSslProvider(); 91 | break; 92 | } 93 | 94 | if (keystorePassword != null) { 95 | builder.keystore(keystore, keystorePassword.toCharArray()); 96 | } else { 97 | builder.keystore(keystore); 98 | } 99 | 100 | if (truststorePassword != null) { 101 | builder.truststore(truststore, truststorePassword); 102 | } else { 103 | builder.truststore(truststore); 104 | } 105 | 106 | return builder.build(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/timeout/TimeoutOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.timeout; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.dropwizard.util.Duration; 5 | import io.lettuce.core.TimeoutOptions; 6 | 7 | public class TimeoutOptionsFactory { 8 | @JsonProperty 9 | private boolean timeoutCommands = TimeoutOptions.DEFAULT_TIMEOUT_COMMANDS; 10 | 11 | @JsonProperty 12 | private boolean applyConnectionTimeout = false; 13 | 14 | @JsonProperty 15 | private Duration fixedTimeout; 16 | 17 | public boolean isTimeoutCommands() { 18 | return timeoutCommands; 19 | } 20 | 21 | public void setTimeoutCommands(final boolean timeoutCommands) { 22 | this.timeoutCommands = timeoutCommands; 23 | } 24 | 25 | public boolean isApplyConnectionTimeout() { 26 | return applyConnectionTimeout; 27 | } 28 | 29 | public void setApplyConnectionTimeout(final boolean applyConnectionTimeout) { 30 | this.applyConnectionTimeout = applyConnectionTimeout; 31 | } 32 | 33 | public Duration getFixedTimeout() { 34 | return fixedTimeout; 35 | } 36 | 37 | public void setFixedTimeout(final Duration fixedTimeout) { 38 | this.fixedTimeout = fixedTimeout; 39 | } 40 | 41 | public TimeoutOptions build() { 42 | final TimeoutOptions.Builder builder = TimeoutOptions.builder() 43 | .timeoutCommands(timeoutCommands); 44 | 45 | if (applyConnectionTimeout) { 46 | builder.connectionTimeout(); 47 | } 48 | 49 | if (fixedTimeout != null) { 50 | builder.fixedTimeout(java.time.Duration.ofMillis(fixedTimeout.toMilliseconds())); 51 | } 52 | 53 | return builder.build(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/topology/ClusterTopologyRefreshOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.topology; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.dropwizard.util.Duration; 5 | import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; 6 | 7 | import java.util.Set; 8 | 9 | import jakarta.validation.constraints.Min; 10 | import jakarta.validation.constraints.NotNull; 11 | 12 | public class ClusterTopologyRefreshOptionsFactory { 13 | @JsonProperty 14 | private boolean periodicRefreshEnabled = ClusterTopologyRefreshOptions.DEFAULT_PERIODIC_REFRESH_ENABLED; 15 | 16 | @NotNull 17 | @JsonProperty 18 | private Duration refreshPeriod = Duration.seconds(ClusterTopologyRefreshOptions.DEFAULT_REFRESH_PERIOD); 19 | 20 | @JsonProperty 21 | private boolean closeStaleConnections = ClusterTopologyRefreshOptions.DEFAULT_CLOSE_STALE_CONNECTIONS; 22 | 23 | @JsonProperty 24 | private boolean dynamicRefreshSources = ClusterTopologyRefreshOptions.DEFAULT_DYNAMIC_REFRESH_SOURCES; 25 | 26 | @NotNull 27 | @JsonProperty 28 | private Set adaptiveRefreshTriggers = 29 | ClusterTopologyRefreshOptions.DEFAULT_ADAPTIVE_REFRESH_TRIGGERS; 30 | 31 | @Min(0) 32 | @JsonProperty 33 | private int refreshTriggersReconnectAttempts = ClusterTopologyRefreshOptions.DEFAULT_REFRESH_TRIGGERS_RECONNECT_ATTEMPTS; 34 | 35 | public boolean isPeriodicRefreshEnabled() { 36 | return periodicRefreshEnabled; 37 | } 38 | 39 | public void setPeriodicRefreshEnabled(final boolean periodicRefreshEnabled) { 40 | this.periodicRefreshEnabled = periodicRefreshEnabled; 41 | } 42 | 43 | public Duration getRefreshPeriod() { 44 | return refreshPeriod; 45 | } 46 | 47 | public void setRefreshPeriod(final Duration refreshPeriod) { 48 | this.refreshPeriod = refreshPeriod; 49 | } 50 | 51 | public boolean isCloseStaleConnections() { 52 | return closeStaleConnections; 53 | } 54 | 55 | public void setCloseStaleConnections(final boolean closeStaleConnections) { 56 | this.closeStaleConnections = closeStaleConnections; 57 | } 58 | 59 | public boolean isDynamicRefreshSources() { 60 | return dynamicRefreshSources; 61 | } 62 | 63 | public void setDynamicRefreshSources(final boolean dynamicRefreshSources) { 64 | this.dynamicRefreshSources = dynamicRefreshSources; 65 | } 66 | 67 | public Set getAdaptiveRefreshTriggers() { 68 | return adaptiveRefreshTriggers; 69 | } 70 | 71 | public void setAdaptiveRefreshTriggers(final Set adaptiveRefreshTriggers) { 72 | this.adaptiveRefreshTriggers = adaptiveRefreshTriggers; 73 | } 74 | 75 | public int getRefreshTriggersReconnectAttempts() { 76 | return refreshTriggersReconnectAttempts; 77 | } 78 | 79 | public void setRefreshTriggersReconnectAttempts(final int refreshTriggersReconnectAttempts) { 80 | this.refreshTriggersReconnectAttempts = refreshTriggersReconnectAttempts; 81 | } 82 | 83 | public ClusterTopologyRefreshOptions build() { 84 | return ClusterTopologyRefreshOptions.builder() 85 | .enablePeriodicRefresh(periodicRefreshEnabled) 86 | .refreshPeriod(java.time.Duration.ofSeconds(refreshPeriod.toSeconds())) 87 | .closeStaleConnections(closeStaleConnections) 88 | .dynamicRefreshSources(dynamicRefreshSources) 89 | .enableAdaptiveRefreshTrigger(adaptiveRefreshTriggers.toArray(new ClusterTopologyRefreshOptions.RefreshTrigger[0])) 90 | .build(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/uri/RedisModeURIFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.uri; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeName; 5 | import com.google.common.net.HostAndPort; 6 | import io.lettuce.core.RedisURI; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotNull; 10 | 11 | @JsonTypeName("redis") 12 | public class RedisModeURIFactory extends RedisURIFactory { 13 | @Valid 14 | @NotNull 15 | @JsonProperty 16 | private HostAndPort node; 17 | 18 | @JsonProperty 19 | private boolean ssl = false; 20 | 21 | @JsonProperty 22 | private boolean startTls = false; 23 | 24 | @JsonProperty 25 | private boolean verifyPeer = true; 26 | 27 | public HostAndPort getNode() { 28 | return node; 29 | } 30 | 31 | public void setNode(final HostAndPort node) { 32 | this.node = node; 33 | } 34 | 35 | public boolean isSsl() { 36 | return ssl; 37 | } 38 | 39 | public void setSsl(final boolean ssl) { 40 | this.ssl = ssl; 41 | } 42 | 43 | public boolean isStartTls() { 44 | return startTls; 45 | } 46 | 47 | public void setStartTls(final boolean startTls) { 48 | this.startTls = startTls; 49 | } 50 | 51 | public boolean isVerifyPeer() { 52 | return verifyPeer; 53 | } 54 | 55 | public void setVerifyPeer(final boolean verifyPeer) { 56 | this.verifyPeer = verifyPeer; 57 | } 58 | 59 | @Override 60 | public RedisURI build() { 61 | final RedisURI.Builder builder = RedisURI.builder() 62 | .withHost(node.getHost()) 63 | .withPort(node.getPort()) 64 | .withSsl(ssl) 65 | .withStartTls(startTls) 66 | .withVerifyPeer(verifyPeer) 67 | .withTimeout(java.time.Duration.ofSeconds(timeout.toSeconds())); 68 | 69 | if (clientName != null) { 70 | builder.withClientName(clientName); 71 | } 72 | 73 | if (username != null && password != null) { 74 | builder.withAuthentication(username, password); 75 | } else if (password != null) { 76 | builder.withPassword(password); 77 | } 78 | 79 | return builder.build(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/uri/RedisURIFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.uri; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 5 | import io.dropwizard.jackson.Discoverable; 6 | import io.dropwizard.util.Duration; 7 | import io.lettuce.core.RedisURI; 8 | 9 | import jakarta.validation.constraints.NotNull; 10 | 11 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 12 | public abstract class RedisURIFactory implements Discoverable { 13 | @NotNull 14 | @JsonProperty 15 | protected Duration timeout = Duration.seconds(RedisURI.DEFAULT_TIMEOUT); 16 | 17 | @JsonProperty 18 | protected String clientName; 19 | 20 | @JsonProperty 21 | protected String username; 22 | 23 | @JsonProperty 24 | protected String password; 25 | 26 | public Duration getTimeout() { 27 | return timeout; 28 | } 29 | 30 | public void setTimeout(final Duration timeout) { 31 | this.timeout = timeout; 32 | } 33 | 34 | public String getClientName() { 35 | return clientName; 36 | } 37 | 38 | public void setClientName(final String clientName) { 39 | this.clientName = clientName; 40 | } 41 | 42 | public String getUsername() { 43 | return username; 44 | } 45 | 46 | public void setUsername(String username) { 47 | this.username = username; 48 | } 49 | 50 | public String getPassword() { 51 | return password; 52 | } 53 | 54 | public void setPassword(final String password) { 55 | this.password = password; 56 | } 57 | 58 | public abstract RedisURI build(); 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/dropwizard/redis/uri/SentinelModeURIFactory.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.uri; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonTypeName; 5 | import com.google.common.net.HostAndPort; 6 | import io.lettuce.core.RedisURI; 7 | import org.hibernate.validator.constraints.NotEmpty; 8 | 9 | import jakarta.validation.Valid; 10 | import java.util.Collections; 11 | import java.util.Set; 12 | 13 | @JsonTypeName("sentinel") 14 | public class SentinelModeURIFactory extends RedisURIFactory { 15 | @Valid 16 | @NotEmpty 17 | @JsonProperty 18 | private Set sentinels = Collections.emptySet(); 19 | 20 | @JsonProperty 21 | private String sentinelMasterId; 22 | 23 | public Set getSentinels() { 24 | return sentinels; 25 | } 26 | 27 | public void setSentinels(final Set sentinels) { 28 | this.sentinels = sentinels; 29 | } 30 | 31 | public String getSentinelMasterId() { 32 | return sentinelMasterId; 33 | } 34 | 35 | public void setSentinelMasterId(final String sentinelMasterId) { 36 | this.sentinelMasterId = sentinelMasterId; 37 | } 38 | 39 | @Override 40 | public RedisURI build() { 41 | final RedisURI.Builder builder = RedisURI.builder() 42 | .withTimeout(java.time.Duration.ofSeconds(timeout.toSeconds())); 43 | 44 | sentinels.forEach(sentinel -> builder.withSentinel(sentinel.getHost(), sentinel.getPort())); 45 | 46 | if (clientName != null) { 47 | builder.withClientName(clientName); 48 | } 49 | 50 | if (username != null && password != null) { 51 | builder.withAuthentication(username, password); 52 | } else if (password != null) { 53 | builder.withPassword(password); 54 | } 55 | 56 | if (sentinelMasterId != null) { 57 | builder.withSentinelMasterId(sentinelMasterId); 58 | } 59 | 60 | return builder.build(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.AbstractRedisClientFactory 2 | io.dropwizard.redis.clientresources.ClientResourcesFactory 3 | io.dropwizard.redis.codec.RedisCodecFactory 4 | io.dropwizard.redis.delay.DelayFactory 5 | io.dropwizard.redis.event.EventBusFactory 6 | io.dropwizard.redis.event.EventLoopGroupProviderFactory 7 | io.dropwizard.redis.metrics.CommandLatencyRecorderFactory 8 | io.dropwizard.redis.metrics.EventPublisherOptionsFactory 9 | io.dropwizard.redis.netty.EventExecutorGroupFactory 10 | io.dropwizard.redis.uri.RedisURIFactory 11 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.AbstractRedisClientFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.RedisClientFactory 2 | io.dropwizard.redis.RedisClusterClientFactory 3 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.clientresources.ClientResourcesFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.clientresources.DefaultClientResourcesFactory 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.codec.RedisCodecFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.codec.ByteArrayCodecFactory 2 | io.dropwizard.redis.codec.CompressionCodecFactory 3 | io.dropwizard.redis.codec.StringCodecFactory 4 | io.dropwizard.redis.codec.Utf8StringCodecFactory 5 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.delay.DelayFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.delay.ConstantDelayFactory 2 | io.dropwizard.redis.delay.DecorrelatedJitterDelayFactory 3 | io.dropwizard.redis.delay.EqualJitterDelayFactory 4 | io.dropwizard.redis.delay.ExponentialDelayFactory 5 | io.dropwizard.redis.delay.FullJitterDelayFactory 6 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.event.EventBusFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.event.DefaultEventBusFactory 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.event.EventLoopGroupProviderFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.event.DefaultEventLoopGroupProviderFactory 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.metrics.CommandLatencyRecorderFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.metrics.DefaultCommandLatencyCollectorFactory 2 | io.dropwizard.redis.metrics.DropwizardCommandLatencyRecorderFactory 3 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.metrics.EventPublisherOptionsFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.metrics.DefaultEventPublisherOptionsFactory 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.netty.EventExecutorGroupFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.netty.DefaultEventExecutorGroupFactory 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.dropwizard.redis.uri.RedisURIFactory: -------------------------------------------------------------------------------- 1 | io.dropwizard.redis.uri.RedisModeURIFactory 2 | io.dropwizard.redis.uri.SentinelModeURIFactory 3 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/clientoptions/ClusterClientOptionsFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.clientoptions; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.dropwizard.configuration.YamlConfigurationFactory; 5 | import io.dropwizard.jackson.Jackson; 6 | import io.dropwizard.jersey.validation.Validators; 7 | import io.lettuce.core.ClientOptions; 8 | import io.lettuce.core.cluster.ClusterClientOptions; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import jakarta.validation.Validator; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | public class ClusterClientOptionsFactoryTest { 16 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 17 | private final Validator validator = Validators.newValidator(); 18 | private final YamlConfigurationFactory configFactory = 19 | new YamlConfigurationFactory<>(ClusterClientOptionsFactory.class, validator, objectMapper, "dw"); 20 | 21 | @Test 22 | public void shouldBuildClusterClientOptions() throws Exception { 23 | final ClusterClientOptionsFactory factory = configFactory.build(); 24 | 25 | assertThat(factory.getSslOptions()) 26 | .isNull(); 27 | assertThat(factory.getMaxRedirects()) 28 | .isEqualTo(ClusterClientOptions.DEFAULT_MAX_REDIRECTS); 29 | assertThat(factory.getTopologyRefreshOptions()) 30 | .isNull(); 31 | assertThat(factory.isAutoReconnect()) 32 | .isEqualTo(ClusterClientOptions.DEFAULT_AUTO_RECONNECT); 33 | assertThat(factory.isValidateClusterNodeMembership()) 34 | .isEqualTo(ClusterClientOptions.DEFAULT_VALIDATE_CLUSTER_MEMBERSHIP); 35 | assertThat(factory.isCancelCommandsOnReconnectFailure()) 36 | .isEqualTo(ClusterClientOptions.DEFAULT_CANCEL_CMD_RECONNECT_FAIL); 37 | assertThat(factory.isPingBeforeActivateConnection()) 38 | .isEqualTo(ClusterClientOptions.DEFAULT_PING_BEFORE_ACTIVATE_CONNECTION); 39 | assertThat(factory.isPublishOnScheduler()) 40 | .isEqualTo(ClusterClientOptions.DEFAULT_PUBLISH_ON_SCHEDULER); 41 | assertThat(factory.isSuspendReconnectOnProtocolFailure()) 42 | .isEqualTo(ClusterClientOptions.DEFAULT_SUSPEND_RECONNECT_PROTO_FAIL); 43 | assertThat(factory.getDisconnectedBehavior()) 44 | .isEqualTo(ClientOptions.DEFAULT_DISCONNECTED_BEHAVIOR); 45 | assertThat(factory.getRequestQueueSize()) 46 | .isEqualTo(ClusterClientOptions.DEFAULT_REQUEST_QUEUE_SIZE); 47 | assertThat(factory.getSocketOptions()) 48 | .isNotNull(); 49 | assertThat(factory.getTimeoutOptions()) 50 | .isNotNull(); 51 | 52 | assertThat(factory.build()).isNotNull(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/clientresources/DefaultClientResourcesFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.clientresources; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.google.common.io.Resources; 6 | import io.dropwizard.configuration.YamlConfigurationFactory; 7 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 8 | import io.dropwizard.jackson.Jackson; 9 | import io.dropwizard.jersey.validation.Validators; 10 | import io.dropwizard.redis.delay.ExponentialDelayFactory; 11 | import io.lettuce.core.resource.ClientResources; 12 | import io.lettuce.core.resource.DefaultClientResources; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import java.io.File; 16 | 17 | import jakarta.validation.Validator; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | public class DefaultClientResourcesFactoryTest { 22 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 23 | private final Validator validator = Validators.newValidator(); 24 | private final YamlConfigurationFactory configFactory = 25 | new YamlConfigurationFactory<>(ClientResourcesFactory.class, validator, objectMapper, "dw"); 26 | 27 | @Test 28 | public void shouldBuildClientResources() throws Exception { 29 | final File yml = new File(Resources.getResource("yaml/clientresources/default.yaml").toURI()); 30 | final ClientResourcesFactory factory = configFactory.build(yml); 31 | assertThat(factory) 32 | .isInstanceOf(DefaultClientResourcesFactory.class); 33 | 34 | final DefaultClientResourcesFactory defaultClientResourcesFactory = (DefaultClientResourcesFactory) factory; 35 | assertThat(defaultClientResourcesFactory.getCommandLatencyRecorder()) 36 | .isNotNull(); 37 | assertThat(defaultClientResourcesFactory.getEventPublisherOptions()) 38 | .isNotNull(); 39 | assertThat(defaultClientResourcesFactory.getEventBusFactory()) 40 | .isNotNull(); 41 | assertThat(defaultClientResourcesFactory.getEventExecutorGroup()) 42 | .isNotNull(); 43 | assertThat(defaultClientResourcesFactory.getEventLoopGroupProvider()) 44 | .isNotNull(); 45 | assertThat(defaultClientResourcesFactory.getComputationThreads()) 46 | .isEqualTo(DefaultClientResources.DEFAULT_COMPUTATION_THREADS); 47 | assertThat(defaultClientResourcesFactory.getIoThreads()) 48 | .isEqualTo(DefaultClientResources.DEFAULT_IO_THREADS); 49 | assertThat(defaultClientResourcesFactory.getDelay()) 50 | .isInstanceOf(ExponentialDelayFactory.class); 51 | 52 | assertThat(defaultClientResourcesFactory.build("name", new MetricRegistry(), null)) 53 | .isInstanceOf(ClientResources.class); 54 | } 55 | 56 | @Test 57 | public void isDiscoverable() { 58 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 59 | .contains(DefaultClientResourcesFactory.class); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/codec/ByteArrayCodecFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.codec; 2 | 3 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public class ByteArrayCodecFactoryTest { 9 | @Test 10 | public void isDiscoverable() { 11 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 12 | .contains(ByteArrayCodecFactory.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/codec/CompressionCodecFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.codec; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 7 | import io.dropwizard.jackson.Jackson; 8 | import io.dropwizard.jersey.validation.Validators; 9 | import io.lettuce.core.codec.CompressionCodec; 10 | import io.lettuce.core.codec.RedisCodec; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import java.io.File; 14 | 15 | import jakarta.validation.Validator; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | 19 | public class CompressionCodecFactoryTest { 20 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 21 | private final Validator validator = Validators.newValidator(); 22 | private final YamlConfigurationFactory configFactory = 23 | new YamlConfigurationFactory<>(RedisCodecFactory.class, validator, objectMapper, "dw"); 24 | 25 | @Test 26 | public void shouldBuildACompressionCodec() throws Exception { 27 | final File yml = new File(Resources.getResource("yaml/codec/compression.yaml").toURI()); 28 | final RedisCodecFactory factory = configFactory.build(yml); 29 | assertThat(factory) 30 | .isInstanceOf(CompressionCodecFactory.class); 31 | 32 | final CompressionCodecFactory compressionCodecFactory = (CompressionCodecFactory) factory; 33 | assertThat(compressionCodecFactory.getCompressionType()) 34 | .isEqualTo(CompressionCodec.CompressionType.GZIP); 35 | assertThat(compressionCodecFactory.getDelegatee()) 36 | .isInstanceOf(StringCodecFactory.class); 37 | assertThat(compressionCodecFactory.build()) 38 | .isInstanceOf(RedisCodec.class); 39 | } 40 | 41 | @Test 42 | public void isDiscoverable() { 43 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 44 | .contains(CompressionCodecFactory.class); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/codec/StringCodecFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.codec; 2 | 3 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public class StringCodecFactoryTest { 9 | @Test 10 | public void isDiscoverable() { 11 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 12 | .contains(StringCodecFactory.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/codec/Utf8StringCodecFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.codec; 2 | 3 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public class Utf8StringCodecFactoryTest { 9 | @Test 10 | public void isDiscoverable() { 11 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 12 | .contains(Utf8StringCodecFactory.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/delay/ConstantDelayFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 7 | import io.dropwizard.jackson.Jackson; 8 | import io.dropwizard.jersey.validation.Validators; 9 | import io.dropwizard.util.Duration; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.File; 13 | 14 | import jakarta.validation.Validator; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | public class ConstantDelayFactoryTest { 19 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 20 | private final Validator validator = Validators.newValidator(); 21 | private final YamlConfigurationFactory configFactory = 22 | new YamlConfigurationFactory<>(DelayFactory.class, validator, objectMapper, "dw"); 23 | 24 | @Test 25 | public void shouldBuildAConstantDelayFactory() throws Exception { 26 | final File yml = new File(Resources.getResource("yaml/delay/constant.yaml").toURI()); 27 | final DelayFactory factory = configFactory.build(yml); 28 | assertThat(factory) 29 | .isInstanceOf(ConstantDelayFactory.class); 30 | 31 | final ConstantDelayFactory constantDelayFactory = (ConstantDelayFactory) factory; 32 | assertThat(constantDelayFactory.getDuration()) 33 | .isEqualTo(Duration.seconds(5)); 34 | } 35 | 36 | @Test 37 | public void isDiscoverable() { 38 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 39 | .contains(ConstantDelayFactory.class); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/delay/DecorrelatedJitterDelayFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 7 | import io.dropwizard.jackson.Jackson; 8 | import io.dropwizard.jersey.validation.Validators; 9 | import io.dropwizard.util.Duration; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.File; 13 | 14 | import jakarta.validation.Validator; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | public class DecorrelatedJitterDelayFactoryTest { 19 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 20 | private final Validator validator = Validators.newValidator(); 21 | private final YamlConfigurationFactory configFactory = 22 | new YamlConfigurationFactory<>(DelayFactory.class, validator, objectMapper, "dw"); 23 | 24 | @Test 25 | public void shouldBuildADecorrelatedJitterDelayFactory() throws Exception { 26 | final File yml = new File(Resources.getResource("yaml/delay/decorrelated-jitter.yaml").toURI()); 27 | final DelayFactory factory = configFactory.build(yml); 28 | assertThat(factory) 29 | .isInstanceOf(DecorrelatedJitterDelayFactory.class); 30 | 31 | final DecorrelatedJitterDelayFactory delayFactory = (DecorrelatedJitterDelayFactory) factory; 32 | assertThat(delayFactory.getBase()).isEqualTo(1L); 33 | assertThat(delayFactory.getLowerBound()).isEqualTo(Duration.seconds(1)); 34 | assertThat(delayFactory.getUpperBound()).isEqualTo(Duration.seconds(5)); 35 | } 36 | 37 | @Test 38 | public void isDiscoverable() { 39 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 40 | .contains(DecorrelatedJitterDelayFactory.class); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/delay/EqualJitterDelayFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 7 | import io.dropwizard.jackson.Jackson; 8 | import io.dropwizard.jersey.validation.Validators; 9 | import io.dropwizard.util.Duration; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.File; 13 | 14 | import jakarta.validation.Validator; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | public class EqualJitterDelayFactoryTest { 19 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 20 | private final Validator validator = Validators.newValidator(); 21 | private final YamlConfigurationFactory configFactory = 22 | new YamlConfigurationFactory<>(DelayFactory.class, validator, objectMapper, "dw"); 23 | 24 | @Test 25 | public void shouldBuildAnEqualJitterDelayFactory() throws Exception { 26 | final File yml = new File(Resources.getResource("yaml/delay/equal-jitter.yaml").toURI()); 27 | final DelayFactory factory = configFactory.build(yml); 28 | assertThat(factory) 29 | .isInstanceOf(EqualJitterDelayFactory.class); 30 | 31 | final EqualJitterDelayFactory delayFactory = (EqualJitterDelayFactory) factory; 32 | assertThat(delayFactory.getBase()).isEqualTo(1L); 33 | assertThat(delayFactory.getLowerBound()).isEqualTo(Duration.seconds(1)); 34 | assertThat(delayFactory.getUpperBound()).isEqualTo(Duration.seconds(5)); 35 | } 36 | 37 | @Test 38 | public void isDiscoverable() { 39 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 40 | .contains(EqualJitterDelayFactory.class); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/delay/ExponentialDelayFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 7 | import io.dropwizard.jackson.Jackson; 8 | import io.dropwizard.jersey.validation.Validators; 9 | import io.dropwizard.util.Duration; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.File; 13 | 14 | import jakarta.validation.Validator; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | public class ExponentialDelayFactoryTest { 19 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 20 | private final Validator validator = Validators.newValidator(); 21 | private final YamlConfigurationFactory configFactory = 22 | new YamlConfigurationFactory<>(DelayFactory.class, validator, objectMapper, "dw"); 23 | 24 | @Test 25 | public void shouldBuildAnExponentialDelayFactory() throws Exception { 26 | final File yml = new File(Resources.getResource("yaml/delay/exponential.yaml").toURI()); 27 | final DelayFactory factory = configFactory.build(yml); 28 | assertThat(factory) 29 | .isInstanceOf(ExponentialDelayFactory.class); 30 | 31 | final ExponentialDelayFactory delayFactory = (ExponentialDelayFactory) factory; 32 | assertThat(delayFactory.getPowersOf()).isEqualTo(3); 33 | assertThat(delayFactory.getLowerBound()).isEqualTo(Duration.seconds(1)); 34 | assertThat(delayFactory.getUpperBound()).isEqualTo(Duration.seconds(5)); 35 | } 36 | 37 | @Test 38 | public void isDiscoverable() { 39 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 40 | .contains(ExponentialDelayFactory.class); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/delay/FullJitterDelayFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.delay; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 7 | import io.dropwizard.jackson.Jackson; 8 | import io.dropwizard.jersey.validation.Validators; 9 | import io.dropwizard.util.Duration; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.File; 13 | 14 | import jakarta.validation.Validator; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | public class FullJitterDelayFactoryTest { 19 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 20 | private final Validator validator = Validators.newValidator(); 21 | private final YamlConfigurationFactory configFactory = 22 | new YamlConfigurationFactory<>(DelayFactory.class, validator, objectMapper, "dw"); 23 | 24 | @Test 25 | public void shouldBuildAFullJitterDelayFactory() throws Exception { 26 | final File yml = new File(Resources.getResource("yaml/delay/full-jitter.yaml").toURI()); 27 | final DelayFactory factory = configFactory.build(yml); 28 | assertThat(factory) 29 | .isInstanceOf(FullJitterDelayFactory.class); 30 | 31 | final FullJitterDelayFactory delayFactory = (FullJitterDelayFactory) factory; 32 | assertThat(delayFactory.getBase()).isEqualTo(1L); 33 | assertThat(delayFactory.getLowerBound()).isEqualTo(Duration.seconds(1)); 34 | assertThat(delayFactory.getUpperBound()).isEqualTo(Duration.seconds(5)); 35 | } 36 | 37 | @Test 38 | public void isDiscoverable() { 39 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 40 | .contains(FullJitterDelayFactory.class); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/event/DefaultEventBusFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.event; 2 | 3 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public class DefaultEventBusFactoryTest { 9 | @Test 10 | public void isDiscoverable() { 11 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 12 | .contains(DefaultEventBusFactory.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/event/DefaultEventLoopGroupProviderFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.event; 2 | 3 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public class DefaultEventLoopGroupProviderFactoryTest { 9 | @Test 10 | public void isDiscoverable() { 11 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 12 | .contains(DefaultEventLoopGroupProviderFactory.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/health/RedisHealthCheckTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.health; 2 | 3 | import io.lettuce.core.RedisException; 4 | import org.assertj.core.api.Assertions; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.mockito.Mock; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | import static org.mockito.Mockito.mock; 11 | import static org.mockito.Mockito.reset; 12 | import static org.mockito.Mockito.when; 13 | 14 | public class RedisHealthCheckTest { 15 | @Mock 16 | private Pingable client = mock(Pingable.class); 17 | 18 | private RedisHealthCheck healthCheck = new RedisHealthCheck(client); 19 | 20 | @BeforeEach 21 | public void setUp() { 22 | reset(client); 23 | } 24 | 25 | @Test 26 | public void shouldReturnHealthyIfPingSucceeds() { 27 | when(client.ping()).thenReturn(RedisHealthCheck.HEALTHY_STRING); 28 | 29 | assertThat(healthCheck.check().isHealthy()).isTrue(); 30 | } 31 | 32 | @Test 33 | public void shouldReturnUnhealthyIfPingFails() { 34 | when(client.ping()).thenReturn("???"); 35 | assertThat(healthCheck.check().isHealthy()).isFalse(); 36 | } 37 | 38 | @Test 39 | public void shouldReturnUnhealthyIfPingThrowsException() { 40 | when(client.ping()).thenThrow(new RedisException("failed for some reason")); 41 | assertThat(healthCheck.check().isHealthy()).isFalse(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/managed/RedisClientManagerTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.managed; 2 | 3 | import io.lettuce.core.AbstractRedisClient; 4 | import io.lettuce.core.api.StatefulConnection; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.mockito.Mockito.mock; 9 | import static org.mockito.Mockito.reset; 10 | import static org.mockito.Mockito.verify; 11 | 12 | public class RedisClientManagerTest { 13 | private static final String NAME = "redis"; 14 | 15 | private final AbstractRedisClient client = mock(AbstractRedisClient.class); 16 | private final StatefulConnection connection = mock(StatefulConnection.class); 17 | 18 | private final RedisClientManager redisClientManager = new RedisClientManager(client, connection, NAME); 19 | 20 | @BeforeEach 21 | public void setUp() throws Exception { 22 | reset(client, connection); 23 | } 24 | 25 | @Test 26 | public void stopShouldCloseClient() throws Exception { 27 | // when 28 | redisClientManager.stop(); 29 | 30 | // then 31 | verify(connection).close(); 32 | verify(client).shutdown(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/metrics/DefaultCommandLatencyCollectorFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.google.common.io.Resources; 6 | import io.dropwizard.configuration.YamlConfigurationFactory; 7 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 8 | import io.dropwizard.jackson.Jackson; 9 | import io.dropwizard.jersey.validation.Validators; 10 | import io.lettuce.core.metrics.DefaultCommandLatencyCollector; 11 | import org.assertj.core.data.Offset; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import java.io.File; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import jakarta.validation.Validator; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | public class DefaultCommandLatencyCollectorFactoryTest { 22 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 23 | private final Validator validator = Validators.newValidator(); 24 | private final YamlConfigurationFactory configFactory = 25 | new YamlConfigurationFactory<>(CommandLatencyRecorderFactory.class, validator, objectMapper, "dw"); 26 | 27 | @Test 28 | public void shouldBuildDefaultCommandLatencyCollector() throws Exception { 29 | final File yml = new File(Resources.getResource("yaml/metrics/default-command-latency-collector.yaml").toURI()); 30 | final CommandLatencyRecorderFactory factory = configFactory.build(yml); 31 | assertThat(factory) 32 | .isInstanceOf(DefaultCommandLatencyCollectorFactory.class); 33 | 34 | final DefaultCommandLatencyCollectorFactory defaultCommandLatencyCollectorFactory = (DefaultCommandLatencyCollectorFactory) factory; 35 | assertThat(defaultCommandLatencyCollectorFactory.getTargetUnit()) 36 | .isEqualTo(TimeUnit.SECONDS); 37 | assertThat(defaultCommandLatencyCollectorFactory.getTargetPercentiles()) 38 | .contains(new double[] { 50.0D, 99.0D }, Offset.offset(0.01D)); 39 | assertThat(defaultCommandLatencyCollectorFactory.isEnabled()) 40 | .isTrue(); 41 | assertThat(defaultCommandLatencyCollectorFactory.isLocalDistinction()) 42 | .isTrue(); 43 | assertThat(defaultCommandLatencyCollectorFactory.isResetLatenciesAfterEvent()) 44 | .isTrue(); 45 | 46 | assertThat(defaultCommandLatencyCollectorFactory.build(new MetricRegistry())) 47 | .isInstanceOf(DefaultCommandLatencyCollector.class); 48 | } 49 | 50 | @Test 51 | public void isDiscoverable() { 52 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 53 | .contains(DefaultCommandLatencyCollectorFactory.class); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/metrics/DefaultEventPublisherOptionsFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 7 | import io.dropwizard.jackson.Jackson; 8 | import io.dropwizard.jersey.validation.Validators; 9 | import io.dropwizard.util.Duration; 10 | import io.lettuce.core.event.DefaultEventPublisherOptions; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import java.io.File; 14 | 15 | import jakarta.validation.Validator; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | 19 | public class DefaultEventPublisherOptionsFactoryTest { 20 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 21 | private final Validator validator = Validators.newValidator(); 22 | private final YamlConfigurationFactory configFactory = 23 | new YamlConfigurationFactory<>(EventPublisherOptionsFactory.class, validator, objectMapper, "dw"); 24 | 25 | @Test 26 | public void shouldBuildDefaultEventPublisherOptions() throws Exception { 27 | final File yml = new File(Resources.getResource("yaml/metrics/default-event-publisher.yaml").toURI()); 28 | final EventPublisherOptionsFactory factory = configFactory.build(yml); 29 | assertThat(factory) 30 | .isInstanceOf(DefaultEventPublisherOptionsFactory.class); 31 | 32 | final DefaultEventPublisherOptionsFactory defaultEventPublisherOptionsFactory = (DefaultEventPublisherOptionsFactory) factory; 33 | assertThat(defaultEventPublisherOptionsFactory.getEventEmitInterval()) 34 | .isEqualTo(Duration.seconds(5)); 35 | 36 | assertThat(defaultEventPublisherOptionsFactory.build()) 37 | .isInstanceOf(DefaultEventPublisherOptions.class); 38 | } 39 | 40 | @Test 41 | public void isDiscoverable() { 42 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 43 | .contains(DefaultEventPublisherOptionsFactory.class); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/metrics/DropwizardCommandLatencyRecorderTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics; 2 | 3 | 4 | import com.codahale.metrics.MetricRegistry; 5 | import com.codahale.metrics.Timer; 6 | import io.lettuce.core.protocol.CommandType; 7 | import io.netty.channel.local.LocalAddress; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.net.SocketAddress; 11 | import java.util.Map; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | 16 | public class DropwizardCommandLatencyRecorderTest { 17 | 18 | private static final SocketAddress LOCAL_ADDRESS = new LocalAddress("localhost:1234"); 19 | 20 | private static final SocketAddress REMOTE_ADDRESS = new LocalAddress("localhost:6379"); 21 | 22 | 23 | @Test 24 | public void verifyMetrics() { 25 | MetricRegistry metricRegistry = new MetricRegistry(); 26 | DropwizardCommandLatencyRecorder commandLatencyRecorder = new DropwizardCommandLatencyRecorder(metricRegistry, true); 27 | 28 | commandLatencyRecorder.recordCommandLatency(LOCAL_ADDRESS, REMOTE_ADDRESS, CommandType.SET, 10, 10); 29 | commandLatencyRecorder.recordCommandLatency(LOCAL_ADDRESS, REMOTE_ADDRESS, CommandType.SET, 100, 500); 30 | 31 | Map timers = metricRegistry.getTimers(); 32 | Timer timer = timers.get("lettuce.command.firstresponse.SET"); 33 | 34 | assertThat(timers.size()).isEqualTo(2); 35 | assertThat(timer.getCount()).isEqualTo(2); 36 | assertThat(timer.getSnapshot().getMean()).isEqualTo(55); 37 | } 38 | 39 | @Test 40 | public void disabled() { 41 | MetricRegistry metricRegistry = new MetricRegistry(); 42 | DropwizardCommandLatencyRecorder commandLatencyRecorder = new DropwizardCommandLatencyRecorder(metricRegistry, false); 43 | 44 | commandLatencyRecorder.recordCommandLatency(LOCAL_ADDRESS, REMOTE_ADDRESS, CommandType.GET, 1, 10); 45 | 46 | assertThat(metricRegistry.getTimers()).isEmpty(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/metrics/event/LettuceMetricsSubscriberTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.metrics.event; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import io.dropwizard.redis.metrics.event.visitor.*; 5 | import io.lettuce.core.cluster.event.ClusterTopologyChangedEvent; 6 | import io.lettuce.core.event.connection.ConnectedEvent; 7 | import io.lettuce.core.event.connection.ConnectionActivatedEvent; 8 | import io.lettuce.core.event.connection.ConnectionDeactivatedEvent; 9 | import io.lettuce.core.event.connection.DisconnectedEvent; 10 | import io.lettuce.core.event.metrics.CommandLatencyEvent; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import java.util.List; 14 | 15 | import static org.mockito.Mockito.*; 16 | 17 | public class LettuceMetricsSubscriberTest { 18 | @Test 19 | public void shouldDispatchEventsToListeners() { 20 | List visitors = ImmutableList.of( 21 | mock(ClusterTopologyChangedEventVisitor.class), 22 | mock(CommandLatencyEventVisitor.class), 23 | mock(ConnectedEventVisitor.class), 24 | mock(ConnectionActivatedEventVisitor.class), 25 | mock(ConnectionDeactivatedEventVisitor.class), 26 | mock(DisconnectedEventVisitor.class) 27 | ); 28 | LettuceMetricsSubscriber subscriber = new LettuceMetricsSubscriber(visitors); 29 | 30 | verifyClusterTopologyChangedEvent(subscriber, visitors); 31 | verifyCommandLatencyEvent(subscriber, visitors); 32 | verifyConnectedEvent(subscriber, visitors); 33 | verifyConnectionActivatedEvent(subscriber, visitors); 34 | verifyConnectionDeactivatedEvent(subscriber, visitors); 35 | verifyDisconnectedEvent(subscriber, visitors); 36 | for (EventVisitor visitor : visitors) { 37 | verifyNoMoreInteractions(visitor); 38 | } 39 | } 40 | 41 | private void verifyClusterTopologyChangedEvent(LettuceMetricsSubscriber subscriber, List visitors) { 42 | ClusterTopologyChangedEvent event = mock(ClusterTopologyChangedEvent.class); 43 | subscriber.accept(event); 44 | 45 | for (EventVisitor visitor : visitors) { 46 | verify(visitor, times(1)).visit(event); 47 | } 48 | } 49 | 50 | private void verifyCommandLatencyEvent(LettuceMetricsSubscriber subscriber, List visitors) { 51 | CommandLatencyEvent event = mock(CommandLatencyEvent.class); 52 | subscriber.accept(event); 53 | 54 | for (EventVisitor visitor : visitors) { 55 | verify(visitor, times(1)).visit(event); 56 | } 57 | } 58 | 59 | private void verifyConnectedEvent(LettuceMetricsSubscriber subscriber, List visitors) { 60 | ConnectedEvent event = mock(ConnectedEvent.class); 61 | subscriber.accept(event); 62 | 63 | for (EventVisitor visitor : visitors) { 64 | verify(visitor, times(1)).visit(event); 65 | } 66 | } 67 | 68 | private void verifyConnectionActivatedEvent(LettuceMetricsSubscriber subscriber, List visitors) { 69 | ConnectionActivatedEvent event = mock(ConnectionActivatedEvent.class); 70 | subscriber.accept(event); 71 | 72 | for (EventVisitor visitor : visitors) { 73 | verify(visitor, times(1)).visit(event); 74 | } 75 | } 76 | 77 | private void verifyConnectionDeactivatedEvent(LettuceMetricsSubscriber subscriber, List visitors) { 78 | ConnectionDeactivatedEvent event = mock(ConnectionDeactivatedEvent.class); 79 | subscriber.accept(event); 80 | 81 | for (EventVisitor visitor : visitors) { 82 | verify(visitor, times(1)).visit(event); 83 | } 84 | } 85 | 86 | private void verifyDisconnectedEvent(LettuceMetricsSubscriber subscriber, List visitors) { 87 | DisconnectedEvent event = mock(DisconnectedEvent.class); 88 | subscriber.accept(event); 89 | 90 | for (EventVisitor visitor : visitors) { 91 | verify(visitor, times(1)).visit(event); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/netty/DefaultEventExecutorGroupFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.netty; 2 | 3 | import com.codahale.metrics.MetricRegistry; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.google.common.io.Resources; 6 | import io.dropwizard.configuration.YamlConfigurationFactory; 7 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 8 | import io.dropwizard.jackson.Jackson; 9 | import io.dropwizard.jersey.validation.Validators; 10 | import io.netty.util.concurrent.DefaultEventExecutorGroup; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import java.io.File; 14 | 15 | import jakarta.validation.Validator; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | 19 | public class DefaultEventExecutorGroupFactoryTest { 20 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 21 | private final Validator validator = Validators.newValidator(); 22 | private final YamlConfigurationFactory configFactory = 23 | new YamlConfigurationFactory<>(EventExecutorGroupFactory.class, validator, objectMapper, "dw"); 24 | 25 | @Test 26 | public void shouldBuildADefaultEventExecutorGroup() throws Exception { 27 | final File yml = new File(Resources.getResource("yaml/netty/default.yaml").toURI()); 28 | final EventExecutorGroupFactory factory = configFactory.build(yml); 29 | assertThat(factory) 30 | .isInstanceOf(DefaultEventExecutorGroupFactory.class); 31 | 32 | final DefaultEventExecutorGroupFactory defaultEventExecutorGroupFactory = (DefaultEventExecutorGroupFactory) factory; 33 | 34 | assertThat(defaultEventExecutorGroupFactory.build(5, "name", new MetricRegistry())) 35 | .isInstanceOf(DefaultEventExecutorGroup.class); 36 | } 37 | 38 | @Test 39 | public void isDiscoverable() { 40 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 41 | .contains(DefaultEventExecutorGroupFactory.class); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/socket/SocketOptionsFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.socket; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.Jackson; 7 | import io.dropwizard.jersey.validation.Validators; 8 | import io.dropwizard.util.Duration; 9 | import io.lettuce.core.SocketOptions; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.File; 13 | 14 | import jakarta.validation.Validator; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | public class SocketOptionsFactoryTest { 19 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 20 | private final Validator validator = Validators.newValidator(); 21 | private final YamlConfigurationFactory configFactory = 22 | new YamlConfigurationFactory<>(SocketOptionsFactory.class, validator, objectMapper, "dw"); 23 | 24 | @Test 25 | public void shouldBuildASocketOptions() throws Exception { 26 | final File yml = new File(Resources.getResource("yaml/socket/socket-options.yaml").toURI()); 27 | final SocketOptionsFactory factory = configFactory.build(yml); 28 | 29 | assertThat(factory.getConnectTimeout()) 30 | .isEqualTo(Duration.seconds(5)); 31 | assertThat(factory.isKeepAlive()) 32 | .isTrue(); 33 | assertThat(factory.isTcpNoDelay()) 34 | .isTrue(); 35 | 36 | assertThat(factory.build()) 37 | .isInstanceOf(SocketOptions.class); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/ssl/SslOptionsFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.ssl; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.Jackson; 7 | import io.dropwizard.jersey.validation.Validators; 8 | import io.lettuce.core.SslOptions; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import java.io.File; 12 | 13 | import jakarta.validation.Validator; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | public class SslOptionsFactoryTest { 18 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 19 | private final Validator validator = Validators.newValidator(); 20 | private final YamlConfigurationFactory configFactory = 21 | new YamlConfigurationFactory<>(SslOptionsFactory.class, validator, objectMapper, "dw"); 22 | 23 | @Test 24 | public void shouldSslOptions() throws Exception { 25 | final File yml = new File(Resources.getResource("yaml/ssl/ssl.yaml").toURI()); 26 | final SslOptionsFactory factory = configFactory.build(yml); 27 | 28 | assertThat(factory.isEnabled()) 29 | .isTrue(); 30 | assertThat(factory.getSslProvider()) 31 | .isEqualTo(SslOptions.DEFAULT_SSL_PROVIDER); 32 | assertThat(factory.getKeystore()) 33 | .isFile(); 34 | assertThat(factory.getKeystorePassword()) 35 | .isEqualTo("hunter2"); 36 | assertThat(factory.getTruststore()) 37 | .isFile(); 38 | assertThat(factory.getTruststorePassword()) 39 | .isEqualTo("hunter2"); 40 | 41 | assertThat(factory.build()).isNotNull(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/test/RedisClusterBundleIT.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.test; 2 | 3 | import com.github.fppt.jedismock.RedisServer; 4 | import io.dropwizard.testing.ConfigOverride; 5 | import io.dropwizard.testing.DropwizardTestSupport; 6 | import io.lettuce.core.api.StatefulRedisConnection; 7 | import org.junit.jupiter.api.AfterAll; 8 | import org.junit.jupiter.api.BeforeAll; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | public class RedisClusterBundleIT { 14 | private static final String CONFIG_PATH = "src/test/resources/yaml/config.yaml"; 15 | 16 | private static final String REDIS_NODE_KEY = "redis.node.node"; 17 | 18 | private static DropwizardTestSupport APP_RULE; 19 | 20 | private static RedisServer REDIS; 21 | 22 | @BeforeAll 23 | public static void setUpBeforeClass() throws Exception { 24 | REDIS = RedisServer.newRedisServer(); // bind to a random port 25 | REDIS.start(); 26 | 27 | APP_RULE = new DropwizardTestSupport<>(TestApplication.class, CONFIG_PATH, 28 | ConfigOverride.config(REDIS_NODE_KEY, String.format("%s:%d", REDIS.getHost(), REDIS.getBindPort()))); 29 | APP_RULE.before(); 30 | } 31 | 32 | @AfterAll 33 | public static void tearDownAfterClass() throws Exception { 34 | APP_RULE.after(); 35 | REDIS.stop(); 36 | } 37 | 38 | @Test 39 | public void shouldHavePinged() { 40 | final StatefulRedisConnection connection = ((TestApplication) APP_RULE.getApplication()).getRedisCluster() 41 | .getConnection(); 42 | 43 | assertThat(connection.sync().get("foo")) 44 | .isEqualTo("bar"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/test/TestApplication.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.test; 2 | 3 | import io.dropwizard.core.Application; 4 | import io.dropwizard.core.setup.Bootstrap; 5 | import io.dropwizard.core.setup.Environment; 6 | import io.dropwizard.redis.RedisClientBundle; 7 | import io.dropwizard.redis.RedisClientFactory; 8 | import io.lettuce.core.api.StatefulRedisConnection; 9 | 10 | public class TestApplication extends Application { 11 | 12 | private final RedisClientBundle redisCluster = 13 | new RedisClientBundle() { 14 | @Override 15 | public RedisClientFactory getRedisClientFactory(final TestConfiguration configuration) { 16 | return configuration.getRedisClientFactory(); 17 | } 18 | }; 19 | 20 | @Override 21 | public void initialize(Bootstrap bootstrap) { 22 | bootstrap.addBundle(redisCluster); 23 | } 24 | 25 | @Override 26 | public void run(final TestConfiguration testConfiguration, final Environment environment) throws Exception { 27 | final StatefulRedisConnection clusterConnection = redisCluster.getConnection(); 28 | 29 | clusterConnection.sync().set("foo", "bar"); 30 | } 31 | 32 | public RedisClientBundle getRedisCluster() { 33 | return redisCluster; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/test/TestConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.test; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.dropwizard.core.Configuration; 5 | import io.dropwizard.redis.RedisClientFactory; 6 | 7 | import jakarta.validation.Valid; 8 | import jakarta.validation.constraints.NotNull; 9 | 10 | public class TestConfiguration extends Configuration { 11 | @Valid 12 | @NotNull 13 | @JsonProperty("redis") 14 | private RedisClientFactory redisClientFactory; 15 | 16 | public RedisClientFactory getRedisClientFactory() { 17 | return redisClientFactory; 18 | } 19 | 20 | public void setRedisClientFactory(final RedisClientFactory redisClientFactory) { 21 | this.redisClientFactory = redisClientFactory; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/timeout/TimeoutOptionsFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.timeout; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.Jackson; 7 | import io.dropwizard.jersey.validation.Validators; 8 | import io.dropwizard.util.Duration; 9 | import io.lettuce.core.TimeoutOptions; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.File; 13 | 14 | import jakarta.validation.Validator; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | public class TimeoutOptionsFactoryTest { 19 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 20 | private final Validator validator = Validators.newValidator(); 21 | private final YamlConfigurationFactory configFactory = 22 | new YamlConfigurationFactory<>(TimeoutOptionsFactory.class, validator, objectMapper, "dw"); 23 | 24 | @Test 25 | public void shouldBuildATimeoutOptions() throws Exception { 26 | final File yml = new File(Resources.getResource("yaml/timeout/timeout-options.yaml").toURI()); 27 | final TimeoutOptionsFactory factory = configFactory.build(yml); 28 | 29 | assertThat(factory.getFixedTimeout()) 30 | .isEqualTo(Duration.seconds(6)); 31 | assertThat(factory.isTimeoutCommands()) 32 | .isTrue(); 33 | assertThat(factory.isApplyConnectionTimeout()) 34 | .isTrue(); 35 | 36 | assertThat(factory.build()) 37 | .isInstanceOf(TimeoutOptions.class); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/topology/ClusterTopologyRefreshOptionsFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.topology; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import io.dropwizard.configuration.YamlConfigurationFactory; 6 | import io.dropwizard.jackson.Jackson; 7 | import io.dropwizard.jersey.validation.Validators; 8 | import io.dropwizard.util.Duration; 9 | import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.File; 13 | 14 | import jakarta.validation.Validator; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | public class ClusterTopologyRefreshOptionsFactoryTest { 19 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 20 | private final Validator validator = Validators.newValidator(); 21 | private final YamlConfigurationFactory configFactory = 22 | new YamlConfigurationFactory<>(ClusterTopologyRefreshOptionsFactory.class, validator, objectMapper, "dw"); 23 | 24 | @Test 25 | public void shouldBuildAClusterToplogyRefreshOptions() throws Exception { 26 | final File yml = new File(Resources.getResource("yaml/topology/cluster-topology-refresh-options.yaml").toURI()); 27 | final ClusterTopologyRefreshOptionsFactory factory = configFactory.build(yml); 28 | 29 | assertThat(factory.isPeriodicRefreshEnabled()) 30 | .isTrue(); 31 | assertThat(factory.getRefreshPeriod()) 32 | .isEqualTo(Duration.minutes(2)); 33 | assertThat(factory.isCloseStaleConnections()) 34 | .isFalse(); 35 | assertThat(factory.isDynamicRefreshSources()) 36 | .isFalse(); 37 | assertThat(factory.getAdaptiveRefreshTriggers()) 38 | .contains(ClusterTopologyRefreshOptions.RefreshTrigger.PERSISTENT_RECONNECTS); 39 | assertThat(factory.getRefreshTriggersReconnectAttempts()) 40 | .isEqualTo(3); 41 | 42 | assertThat(factory.build()) 43 | .isInstanceOf(ClusterTopologyRefreshOptions.class); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/uri/RedisModeURIFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.uri; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import com.google.common.net.HostAndPort; 6 | import io.dropwizard.configuration.YamlConfigurationFactory; 7 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 8 | import io.dropwizard.jackson.Jackson; 9 | import io.dropwizard.jersey.validation.Validators; 10 | import io.dropwizard.util.Duration; 11 | import io.lettuce.core.RedisURI; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import java.io.File; 15 | 16 | import jakarta.validation.Validator; 17 | 18 | import static org.assertj.core.api.Assertions.assertThat; 19 | 20 | public class RedisModeURIFactoryTest { 21 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 22 | private final Validator validator = Validators.newValidator(); 23 | private final YamlConfigurationFactory configFactory = 24 | new YamlConfigurationFactory<>(RedisURIFactory.class, validator, objectMapper, "dw"); 25 | 26 | @Test 27 | public void shouldBuildARedisURI() throws Exception { 28 | final File yml = new File(Resources.getResource("yaml/uri/redis-uri.yaml").toURI()); 29 | final RedisURIFactory factory = configFactory.build(yml); 30 | assertThat(factory) 31 | .isInstanceOf(RedisModeURIFactory.class); 32 | 33 | final RedisModeURIFactory redisModeURIFactory = (RedisModeURIFactory) factory; 34 | 35 | assertThat(redisModeURIFactory.getNode()) 36 | .isEqualTo(HostAndPort.fromParts("127.0.0.1", 6379)); 37 | assertThat(redisModeURIFactory.getTimeout()) 38 | .isEqualTo(Duration.seconds(90)); 39 | assertThat(redisModeURIFactory.getClientName()) 40 | .isEqualTo("test"); 41 | assertThat(redisModeURIFactory.getPassword()) 42 | .isEqualTo("hunter2"); 43 | assertThat(redisModeURIFactory.isSsl()) 44 | .isTrue(); 45 | assertThat(redisModeURIFactory.isStartTls()) 46 | .isTrue(); 47 | assertThat(redisModeURIFactory.isVerifyPeer()) 48 | .isTrue(); 49 | 50 | assertThat(factory.build()) 51 | .isInstanceOf(RedisURI.class); 52 | } 53 | 54 | @Test 55 | public void isDiscoverable() { 56 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 57 | .contains(RedisModeURIFactory.class); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/io/dropwizard/redis/uri/SentinelModeURIFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.dropwizard.redis.uri; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.io.Resources; 5 | import com.google.common.net.HostAndPort; 6 | import io.dropwizard.configuration.YamlConfigurationFactory; 7 | import io.dropwizard.jackson.DiscoverableSubtypeResolver; 8 | import io.dropwizard.jackson.Jackson; 9 | import io.dropwizard.jersey.validation.Validators; 10 | import io.dropwizard.redis.delay.ConstantDelayFactory; 11 | import io.dropwizard.util.Duration; 12 | import io.lettuce.core.RedisURI; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import java.io.File; 16 | 17 | import jakarta.validation.Validator; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | public class SentinelModeURIFactoryTest { 22 | private final ObjectMapper objectMapper = Jackson.newObjectMapper(); 23 | private final Validator validator = Validators.newValidator(); 24 | private final YamlConfigurationFactory configFactory = 25 | new YamlConfigurationFactory<>(RedisURIFactory.class, validator, objectMapper, "dw"); 26 | 27 | @Test 28 | public void shouldBuildASentinelRedisURI() throws Exception { 29 | final File yml = new File(Resources.getResource("yaml/uri/sentinel-redis-uri.yaml").toURI()); 30 | final RedisURIFactory factory = configFactory.build(yml); 31 | 32 | assertThat(factory) 33 | .isInstanceOf(SentinelModeURIFactory.class); 34 | 35 | final SentinelModeURIFactory sentinelModeURIFactory = (SentinelModeURIFactory) factory; 36 | 37 | assertThat(sentinelModeURIFactory.getTimeout()) 38 | .isEqualTo(Duration.seconds(90)); 39 | assertThat(sentinelModeURIFactory.getClientName()) 40 | .isEqualTo("test"); 41 | assertThat(sentinelModeURIFactory.getPassword()) 42 | .isEqualTo("hunter2"); 43 | assertThat(sentinelModeURIFactory.getSentinels()) 44 | .contains(HostAndPort.fromParts("127.0.0.1", 6389)); 45 | assertThat(sentinelModeURIFactory.getSentinelMasterId()) 46 | .isEqualTo("abc"); 47 | 48 | assertThat(factory.build()) 49 | .isInstanceOf(RedisURI.class); 50 | } 51 | 52 | @Test 53 | public void isDiscoverable() { 54 | assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes()) 55 | .contains(SentinelModeURIFactory.class); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/resources/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropwizard/dropwizard-redis/420d22b093e8b78539ba2bdffd403bfc33517f69/src/test/resources/keystore.p12 -------------------------------------------------------------------------------- /src/test/resources/truststore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropwizard/dropwizard-redis/420d22b093e8b78539ba2bdffd403bfc33517f69/src/test/resources/truststore.p12 -------------------------------------------------------------------------------- /src/test/resources/yaml/clientresources/default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: default 3 | -------------------------------------------------------------------------------- /src/test/resources/yaml/codec/compression.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: compression 3 | delegatee: 4 | type: string 5 | compressionType: GZIP 6 | -------------------------------------------------------------------------------- /src/test/resources/yaml/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | redis: 3 | type: basic 4 | name: my-redis-use-case 5 | node: 6 | type: redis 7 | node: "127.0.0.1:6379" 8 | clientName: person-app 9 | redisCodec: 10 | type: string 11 | clientResources: 12 | type: default 13 | commandLatencyRecorder: 14 | type: default 15 | enabled: false 16 | 17 | server: 18 | type: simple 19 | applicationContextPath: / 20 | connector: 21 | type: http 22 | port: 0 23 | -------------------------------------------------------------------------------- /src/test/resources/yaml/delay/constant.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: constant 3 | duration: 5s 4 | -------------------------------------------------------------------------------- /src/test/resources/yaml/delay/decorrelated-jitter.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: decorrelated-jitter 3 | base: 1 4 | lowerBound: 1s 5 | upperBound: 5s 6 | -------------------------------------------------------------------------------- /src/test/resources/yaml/delay/equal-jitter.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: equal-jitter 3 | base: 1 4 | lowerBound: 1s 5 | upperBound: 5s 6 | -------------------------------------------------------------------------------- /src/test/resources/yaml/delay/exponential.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: exponential 3 | powersOf: 3 4 | lowerBound: 1s 5 | upperBound: 5s 6 | -------------------------------------------------------------------------------- /src/test/resources/yaml/delay/full-jitter.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: full-jitter 3 | base: 1 4 | lowerBound: 1s 5 | upperBound: 5s 6 | -------------------------------------------------------------------------------- /src/test/resources/yaml/metrics/default-command-latency-collector.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: default 3 | targetUnit: SECONDS 4 | targetPercentiles: [50.0, 99.0] 5 | resetLatenciesAfterEvent: true 6 | localDistinction: true 7 | enabled: true 8 | -------------------------------------------------------------------------------- /src/test/resources/yaml/metrics/default-event-publisher.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: default 3 | eventEmitInterval: 5s 4 | -------------------------------------------------------------------------------- /src/test/resources/yaml/netty/default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: default 3 | -------------------------------------------------------------------------------- /src/test/resources/yaml/socket/socket-options.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | connectTimeout: 5s 3 | keepAlive: true 4 | tcpNoDelay: true 5 | -------------------------------------------------------------------------------- /src/test/resources/yaml/ssl/ssl.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | enabled: true 3 | keystore: src/test/resources/keystore.p12 4 | keystorePassword: hunter2 5 | truststore: src/test/resources/truststore.p12 6 | truststorePassword: hunter2 7 | -------------------------------------------------------------------------------- /src/test/resources/yaml/timeout/timeout-options.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | timeoutCommands: true 3 | applyConnectionTimeout: true 4 | fixedTimeout: 6s 5 | -------------------------------------------------------------------------------- /src/test/resources/yaml/topology/cluster-topology-refresh-options.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | periodicRefreshEnabled: true 3 | refreshPeriod: 2m 4 | closeStaleConnections: false 5 | dynamicRefreshSources: false 6 | adaptiveRefreshTriggers: 7 | - PERSISTENT_RECONNECTS 8 | refreshTriggersReconnectAttempts: 3 9 | -------------------------------------------------------------------------------- /src/test/resources/yaml/uri/redis-uri.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: redis 3 | node: "127.0.0.1:6379" 4 | timeout: 90s 5 | clientName: test 6 | password: hunter2 7 | ssl: true 8 | startTls: true 9 | verifyPeer: true 10 | -------------------------------------------------------------------------------- /src/test/resources/yaml/uri/sentinel-redis-uri.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: sentinel 3 | timeout: 90s 4 | clientName: test 5 | password: hunter2 6 | sentinels: 7 | - "127.0.0.1:6389" 8 | sentinelMasterId: abc 9 | --------------------------------------------------------------------------------